Возможно ли смоделировать вывод метода модели в Laravel / PHPUnit? - PullRequest
2 голосов
/ 21 июня 2019

Можно ли смоделировать или подделать вывод метода модели в 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, что когда этот метод запускается (он вызывается в контроллере), возвращать true или false, если я намеренно хочу, чтобы он потерпел неудачу. В тесте я заводил веб-сайт, и, конечно, он потерпит неудачу с помощью метода php dns_get_record.

Я прочитал документы Laravel и отыскивал в Google информацию о методах насмешливой модели, но, похоже, не могу найти ничего, кроме как обернуть большую if вокруг метода, который проверяет, не нахожусь ли я в режиме тестирования, и если я просто верну истину. <- ЮК. </p>

UPDATE Это пример того, как я вызываю метод в контроллере

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
    );

Метод контроллера принимает привязки веб-сайта и модели домена. если я dd(get_class($website)) вверху контроллера, он отображает пространство имен для фактической модели, а не макет.

1 Ответ

0 голосов
/ 21 июня 2019

В вашем коде вы вводите свою модель в контроллер в качестве параметра, это означает, что Ioc Laravel будет запрошен для экземпляра модели Website и предоставит его при выполнении реального кода.

В своем тесте вы можете создать макет и затем сказать ioc вернуть этот макет при запросе экземпляра Website, например:

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

перед выполнением вашего контроллера. Теперь при выполнении контроллера в вашем тесте имитация будет той, которая будет принимать вызовы методов, и вы можете настроить его так, чтобы он ожидал и подделывал ответы. В частности, в вашем коде ваш макет должен ожидать и подделывать два вызова, например:

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

Редактирование всего кода (я придумал некоторые вещи, такие как маршрут или имя контроллера, которые не важны, используйте свой собственный).

Контроллер

<?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;
...