Как издеваться над моделью без подключения базы данных в Laravel - PullRequest
0 голосов
/ 27 октября 2019

Я пытаюсь проверить метод index() контроллера. В этом методе есть модель.

class UserController extends Controller
{        
     public function index()
     {
        return User::all();
     }
}

В тестовом классе у меня есть следующее.

class UserControllerTest extends TestCase
{
    public function testIndex():void
    {
        $user = factory(User::class)->make();
        $mock = Mockery::mock(User::class);
        $mock->shouldReceive('all')->andReturn($user);
        $this->app->instance('User', $mock);
        $response = $this->json('GET', 'api/users');
        dd($response->getContent()); // error : [2002] Connection refused
    }

}

При запуске теста у меня возникает ошибка при подключении кбаза данных. Это странно, потому что я издевался над моделью, что означало бы, что мне не нужно устанавливать соединение с базой данных. Как я могу решить эту ошибку?

Ошибка

SQLSTATE [HY000] [2002] Соединение отклонено (SQL: выберите * из users, где users. deleted_at равно нулю)

1 Ответ

0 голосов
/ 28 октября 2019

Вы пытаетесь смоделировать статический вызов с помощью подхода для имитации вызова функции для экземпляра объекта. Насмешивать вызовы статических функций не просто, это можно сделать с помощью псевдонимов, но это не рекомендуется.

Простой подход - просто обернуть свою логику в службу и смоделировать ее.

class UserService {

    public function all(): Collection {
        return User::all();
    }
}

Теперь смоделированный код должен выглядеть следующим образом.

    $user = factory(User::class)->make();
    $mock = Mockery::mock(UserService::class);
    // Teoretically all method will return Eloquent Collection, but should be fine 
    $mock->shouldReceive('all')->andReturn(collect($user));
    $this->app->instance(UserService::class, $mock);

При использовании container и замене экземпляров очень зависит, что вы получите эти макеты с помощью container, а не new ключевое слово. Таким образом, контроллер должен выглядеть примерно так:

class UserController extends Controller
{
    /** @var UserService **/
    private $userService;

    public function __construct(UserService $userService) {
        // load userService from the container as the mocked instance on tests
        $this->userService = $userService;
    }

    public function index()
    {
        return $this->userService->all();
    }
}

Последнее замечание: я работал над несколькими проектами в качестве основного драйвера тестирования, покрытия кода и т. Д., Гораздо проще создавать тесты, имея базу данныхтам либо используйте sqlite, либо предоставьте среду docker, чтобы предоставить базу данных для вас. Тестирование без него - скорее препятствие, чем предоставление чего-либо значительного. Скорость имеет решающее значение в вашем подходе к тестированию, потому что его будет много, и лучше сделать это быстрее, чем пропустить его из-за нехватки времени, и насмешка над всеми вызовами БД будет трудной.

...