php - 可以在 Laravel/PHPUnit 中模拟模型方法输出吗?

标签 php laravel testing mocking phpunit

是否可以在 Laravel 5.8 中模拟或伪造模型方法的输出?

例如,考虑这个模型

class Website extends Model
 {
     public function checkDomainConfiguration($domain): bool
     {
         try {
             $records = dns_get_record($domain, DNS_A);
         } catch (ErrorException $e) {
             return false;
         }

         if (isset($records[0]['ip']) && $records[0]['ip'] === $this->server->ipv4_address) {
             return true;
         }

         return false;
     }
 }

出于测试的目的,我需要告诉 phpunit 当此方法触发时(它在 Controller 中调用)返回 true,如果我故意希望它失败则返回 false。在测试中我建立了一个网站,当然它会失败 php 方法 dns_get_record

我已经阅读了 Laravel 文档,并搜索了关于模拟模型方法的谷歌,但似乎找不到任何东西,除了在检查我是否不在测试模式下,如果我只是返回 true。 <-- 呸。

更新 这是我如何在 Controller 中调用方法的示例

class SomeController extends Controller
{
    public function store(Website $website, Domain $domain)
    {
        if (! $website->checkDomainConfiguration($domain->domain)) {
            return response([
                'error' => 'error message'
            ], 500);
        }

        // continue on here if all good.
    }
}

这是测试的一些代码

$website = factory(Website::class)->create();
$domain = factory(Domain::class)->create([
    'website_id' => $website->id
]);
//Mock the website object
$websiteMock = \Mockery::mock(Website::class)->makePartial();
$websiteMock->shouldReceive('getAttribute')
    ->once()
    ->with('domain')
    ->andReturn($website->domain);

$websiteMock->shouldReceive('checkDomainConfiguration')
    ->with($domain->domain)
    ->andReturn(true);

app()->instance(Website::class, $websiteMock);

// tried end point like this
    $response = $this->json(
        'POST',
        'api/my-end-point/websites/' . $website->domain . '/domain/' . $domain->id
    );
    //also tried like this
    $response = $this->json(
        'POST',
        'api/my-end-point/websites/' . $websiteMock->domain . '/domain/' . $domain->id
    );

Controller 方法接受网站和域模型绑定(bind)。如果我在 Controller 的顶部 dd(get_class($website)),它会显示实际模型的 namespace ,而不是模拟。

最佳答案

在您的代码中,您将模型作为参数注入(inject) Controller ,这意味着 Laravel 的 ioc 将被要求提供 Website 模型的实例,并将在执行实际代码时提供它。

在您的测试中,您可以创建一个模拟,然后告诉 ioc 在请求 Website 实例时返回该模拟,例如:

$websiteMock = Mockery::mock(Website::class);
app()->instance(Website::class, $websitemock)

在执行你的 Controller 之前。现在,当在您的测试中执行 Controller 时,模拟将是接收方法调用的那个,您可以将其设置为期望它们并伪造响应。特别是在您的代码上,您的模拟应该期望并伪造两个调用,例如:

$websiteMock->shouldReceive('getAttribute')
    ->once()
    ->with('domain')
    ->andReturn('test.domain'):
$websiteMock->shouldReceive('checkDomainConfiguration')
    ->once()
    ->with('test.domain')
    ->andReturn(false):

编辑整个代码(我已经编造了一些不重要的东西,比如路由或 Controller 名称,使用你自己的)。

Controller

<?php

namespace App\Http\Controllers;

use App\Domain;
use App\Website;

class StackOverflowController extends Controller
{
    public function store(Website $website, Domain $domain)
    {
        if (! $website->checkDomainConfiguration($domain->domain)) {
            return response([
                'error' => 'error message'
            ], 500);
        }
    }
}

集成测试

public function testStackOverflow()
{
    $website = Mockery::mock(Website::class);
    app()->instance(Website::class, $website);
    $domain = Mockery::mock(Domain::class);
     app()->instance(Domain::class, $domain);

    $domain->shouldReceive('getAttribute')
        ->once()
        ->with('domain')
        ->andReturn('some.domain');

    $website->shouldReceive('checkDomainConfiguration')
        ->once()
        ->with('some.domain')
        ->andReturn(false);
    $response = $this->json('POST', '/stack/websiteId/overflow/domainId');
    $this->assertEquals('error message', json_decode($response->getContent())->error);
    $this->assertEquals(500, $response->status());
}

我也没有包含集成测试文件的开头,但仔细检查你是否包含:

use App\Domain;
use App\Website;

关于php - 可以在 Laravel/PHPUnit 中模拟模型方法输出吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56700224/

相关文章:

php - 在 WooCommerce 客户订单详细信息页面中添加并保存文本字段

php - 非常简单的 PHP 代码使 Apache 崩溃

php - 如何在 Laravel 流明中调用 app_path() 函数?

php - 在 PHP 中缓存函数结果

php - Pluck id(整数)转换为字符串 Laravel

javascript - 在移动浏览器中测试 Javascript

testing - 如何使用电梯应用程序进行测试?

unit-testing - 我们应该在类级别还是函数级别创建任何类的对象

php - AJAX/Laravel 多个文件上传

php - 在 Laravel 5 中扩展 Request 类