【发布时间】:2017-06-11 08:23:15
【问题描述】:
我在基于 Laravel 的 PHP 项目中使用 Mockery 来帮助测试 Laravel MVC 控制器。下面是我正在尝试测试的控制器类的相关部分。
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.
但有趣和令人困惑的是,如果我注释掉并组合 3 个嘲弄部分中的 2 个,我的测试通过了。这意味着,我的代码实际上工作正常,但由于某种原因,在这种情况下,我无法将多个模拟模型对象注入我的控制器并一次测试它们。我猜我可以将其拆分为三个单独的测试,以确保调用每个模型的add(...) 函数,但如果可能的话,我想在一个测试用例中完成所有操作。我也知道我可以使用存储库模式将控制器的 add(...) 函数中的所有业务逻辑包装到一个调用中,但是在测试存储库类时我会遇到同样的问题。
【问题讨论】:
-
尝试在 setUp 中绑定 mock,而不是在 test 函数中。
-
我试过你的建议,我得到了完全相同的行为。我认为我不希望在
setUp中绑定我的模拟,因为我不需要为我的大多数测试用例共享这些模拟。 -
路由是怎么定义的?
-
我只是尝试复制您的代码并使用
mockery/mockery 0.9.7和laravel/framework 5.3.30以及5.4.4运行相同的测试。模拟被正确注入并且测试通过没有问题。你用的是什么版本的 Mockery 和 Laravel? -
我降级到您的确切版本,一切仍然有效。这是代码github.com/nCrazed/SO-41863723
标签: php laravel phpunit mockery