Я использую Mockery в моем проекте на базе Laravel, чтобы помочь протестировать контроллер Laravel MVC. Ниже приведена соответствующая часть моего класса контроллера, который я пытаюсь проверить.Проблема с mockery при введении нескольких смешных объектов в контроллер
class DevicesController extends Controller
{
private $deviceModel;
private $rfDeviceModel;
private $userModel;
private $userDeviceModel;
public function __construct(Device $deviceModel, RFDevice $rfDeviceModel, User $userModel, UserDevice $userDeviceModel)
{
$this->middleware('guest');
$this->deviceModel = $deviceModel;
$this->rfDeviceModel = $rfDeviceModel;
$this->userModel = $userModel;
$this->userDeviceModel = $userDeviceModel;
}
...
public function add(Request $request)
{
$name = $request->input('name');
$description = $request->input('description');
$onCode = $request->input('onCode');
$offCode = $request->input('offCode');
$pulseLength = $request->input('pulseLength');
$type = 1;
$currentUserId = $this->currentUser()->id;
$newDeviceId = $this->deviceModel->add($name, $description, $type)->id;
$this->rfDeviceModel->add($onCode, $offCode, $pulseLength, $newDeviceId);
$this->userDeviceModel->add($currentUserId, $newDeviceId);
return redirect()->route('devices');
}
}
В частности, я пишу несколько модульных тестов вокруг add(Request $request)
функции контроллера, чтобы убедиться, что каждая из трех моделей add(...)
функции называются. Мой тест, чтобы справиться с этим выглядит следующим образом:
public function testAdd_CallsAddForModels()
{
$mockDeviceModel = Mockery::mock(Device::class);
$mockDeviceModel->shouldReceive('add')->withAnyArgs()->once();
$this->app->instance(Device::class, $mockDeviceModel);
$mockRFDeviceModel = Mockery::mock(RFDevice::class);
$mockRFDeviceModel->shouldReceive('add')->withAnyArgs()->once();
$this->app->instance(RFDevice::class, $mockRFDeviceModel);
$mockUserDeviceModel = Mockery::mock(UserDevice::class);
$mockUserDeviceModel->shouldReceive('add')->withAnyArgs()->once();
$this->app->instance(UserDevice::class, $mockUserDeviceModel);
$user = $this->givenSingleUserExists();
$this->addDeviceForUser($user->user_id);
}
private function givenSingleUserExists()
{
$user = new User;
$name = self::$faker->name();
$email = self::$faker->email();
$userId = self::$faker->uuid();
$user = $user->add($name, $email, $userId);
return $user;
}
private function addDeviceForUser($userId)
{
$this->withSession([env('SESSION_USER_ID') => $userId])
->call('POST', '/devices/add', [
'name' => 'Taylor',
'description' => 'abcd',
'onCode' => 1,
'offCode' => 2,
'pulseLength' => 3
]);
}
Когда я запускаю этот тест, я получаю следующий вывод в консоли:
There was 1 error:
1) Tests\Unit\Controller\DeviceControllerTest::testAdd_CallsAddForModels
Mockery\Exception\InvalidCountException: Method add() from Mockery_1_App_RFDevice should be called
exactly 1 times but called 0 times.
Но самое смешное и озадачивает дело в том, что если я комментарий и комбинация из 2 из 3 разделов издевательств, мой тестовый проход. Это означает, что мой код действительно работает правильно, но по какой-то причине в этом случае я не могу вставлять в проект один из нескольких моделей объектов модели и проверять их все сразу. I guess Я мог бы разделить это на три отдельных теста, которые гарантируют, что функция каждой модели add(...)
вызывается, но я хочу сделать все это в одном случае, если это возможно. Я также знаю, что могу использовать шаблон репозитория, чтобы обернуть всю бизнес-логику в функции контроллера add(...)
в один вызов, но затем я столкнулся бы с той же проблемой при тестировании класса репозитория.
Попробуйте привязать mocks в setUp вместо тестовой функции. – nCrazed
Я попробовал ваше предложение, я получаю то же самое поведение. Я не думаю, что мне хотелось бы привязать свои издевательства в 'setUp', так как мне не нужно делиться этими mocks для большинства моих тестовых случаев. – roundtheworld
Как определяется маршрут? – nCrazed