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

Joh*_*sey 8 php testing phpunit mocking laravel

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;
     }
 }
Run Code Online (Sandbox Code Playgroud)

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

我已经阅读了 Laravel 文档,并在 google 上搜索了有关模拟模型方法的信息,但似乎找不到任何内容,除了在if方法周围封装一个大的内容来检查我是否处于测试模式,以及我是否只是返回真的。<-- 好极了。

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

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.
    }
}
Run Code Online (Sandbox Code Playgroud)

这是测试中的一些代码

$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
    );
Run Code Online (Sandbox Code Playgroud)

控制器方法接受网站和域模型绑定。如果我dd(get_class($website))位于控制器的顶部,它会显示实际模型的命名空间,而不是模拟模型。

nam*_*via 6

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

在您的测试中,您可以创建一个模拟,然后告诉国际奥委会在请求类似实例时返回该模拟Website

$websiteMock = Mockery::mock(Website::class);
app()->instance(Website::class, $websitemock)
Run Code Online (Sandbox Code Playgroud)

在执行你的控制器之前。现在,当在测试中执行控制器时,模拟将接收方法调用,您可以将其设置为期望它们并伪造响应。具体来说,在您的代码中,您的模拟应该预期并伪造两个调用,例如:

$websiteMock->shouldReceive('getAttribute')
    ->once()
    ->with('domain')
    ->andReturn('test.domain'):
$websiteMock->shouldReceive('checkDomainConfiguration')
    ->once()
    ->with('test.domain')
    ->andReturn(false):
Run Code Online (Sandbox Code Playgroud)

使用整个代码进行编辑(我编写了一些不重要的内容,例如路由或控制器名称,请使用您自己的)。

控制器

<?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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

集成测试

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());
}
Run Code Online (Sandbox Code Playgroud)

另外,我没有包含集成测试文件的开头,但请仔细检查您是否包含:

use App\Domain;
use App\Website;
Run Code Online (Sandbox Code Playgroud)