如何对 laravel 表单请求的 prepareForValidation 进行单元测试?
How to unit test prepareForValidation on laravel Form Request?
背景: 在我的Laravel项目中,我正在实现管理员可以创建用户的用户创建功能。在创建用户期间,我设置了一个随机密码(不是输入)并向用户发送密码重置电子邮件。在我的架构中,密码字段不可为空(我不希望数据库中有任何没有密码的用户)。
我正在使用表单请求来实现控制器验证,并使用 prepareForValidation 在 POST 上设置随机密码,但在 [=31= 上将密码设置为 null ]PATCH 因为我不希望在用户更新事件时更新密码。 (管理员可以更新用户配置文件,但不能更新密码。)验证规则 'sometimes' 处理这两种情况。
以下来自我的请求 class(rules 和 prepareForValidation 方法)。
class AdminUserRequest extends FormRequest
{
public function rules()
{
return [
...
'password' => [
'sometimes',
'required',
'min:8',
],
...
];
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
if ($this->getMethod() == 'POST') {
$this->merge([
'password' => Hash::make(Str::random(10)),
]);
} else {
$this->offsetUnset('password');
}
}
}
问题:
我的问题是在表单请求中单元测试 prepareForValidation 功能的最佳方法是什么。
应该测试请求数据集在 POST 上有密码并且没有在其他方法上设置。
当然我可以做功能测试来覆盖案例
- POST 路由在数据库中创建用户并且在会话中没有错误
- PATCH to route 不会更改密码,即使它已传入数据集。
但我想知道是否有办法对此进行单元测试。以及如何去做。
提前致谢
它可能是这样的(我没有嘲笑 request
并对其安排期望 - 而是保持简单)。最后一个方法用于 private/protected
方法调用 classes。这可以移至 TestCase
class 以供将来使用。
class AdminUserRequestTest extends TestCase
{
/**
* @test
* @covers ::prepareForValidation
*/
function it_should_add_password_when_it_is_post_method()
{
$request = new AdminUserRequest();
$request->setMethod('POST');
$hashedPassword = 'a1b2c3d4e5';
Hash::shouldReceive('make')->andReturn($hashedPassword);
$this->invokeMethod($request, 'prepareForValidation');
$this->assertSame($request->get('password'), $hashedPassword);
}
/**
* @test
* @covers ::prepareForValidation
*/
function it_should_unset_password_when_it_is_not_post_method()
{
$request = new AdminUserRequest();
$request->setMethod('PUT'); // it could be something else besides POST
$this->invokeMethod($request, 'prepareForValidation');
$this->assertFalse($request->has('password'));
}
protected function invokeMethod(&$object, $methodName, array $parameters = [])
{
$reflection = new ReflectionClass(get_class($object));
$method = $reflection->getMethod($methodName);
$method->setAccessible(true);
return $method->invokeArgs($object, $parameters);
}
}
背景: 在我的Laravel项目中,我正在实现管理员可以创建用户的用户创建功能。在创建用户期间,我设置了一个随机密码(不是输入)并向用户发送密码重置电子邮件。在我的架构中,密码字段不可为空(我不希望数据库中有任何没有密码的用户)。
我正在使用表单请求来实现控制器验证,并使用 prepareForValidation 在 POST 上设置随机密码,但在 [=31= 上将密码设置为 null ]PATCH 因为我不希望在用户更新事件时更新密码。 (管理员可以更新用户配置文件,但不能更新密码。)验证规则 'sometimes' 处理这两种情况。
以下来自我的请求 class(rules 和 prepareForValidation 方法)。
class AdminUserRequest extends FormRequest
{
public function rules()
{
return [
...
'password' => [
'sometimes',
'required',
'min:8',
],
...
];
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
if ($this->getMethod() == 'POST') {
$this->merge([
'password' => Hash::make(Str::random(10)),
]);
} else {
$this->offsetUnset('password');
}
}
}
问题: 我的问题是在表单请求中单元测试 prepareForValidation 功能的最佳方法是什么。
应该测试请求数据集在 POST 上有密码并且没有在其他方法上设置。
当然我可以做功能测试来覆盖案例
- POST 路由在数据库中创建用户并且在会话中没有错误
- PATCH to route 不会更改密码,即使它已传入数据集。
但我想知道是否有办法对此进行单元测试。以及如何去做。
提前致谢
它可能是这样的(我没有嘲笑 request
并对其安排期望 - 而是保持简单)。最后一个方法用于 private/protected
方法调用 classes。这可以移至 TestCase
class 以供将来使用。
class AdminUserRequestTest extends TestCase
{
/**
* @test
* @covers ::prepareForValidation
*/
function it_should_add_password_when_it_is_post_method()
{
$request = new AdminUserRequest();
$request->setMethod('POST');
$hashedPassword = 'a1b2c3d4e5';
Hash::shouldReceive('make')->andReturn($hashedPassword);
$this->invokeMethod($request, 'prepareForValidation');
$this->assertSame($request->get('password'), $hashedPassword);
}
/**
* @test
* @covers ::prepareForValidation
*/
function it_should_unset_password_when_it_is_not_post_method()
{
$request = new AdminUserRequest();
$request->setMethod('PUT'); // it could be something else besides POST
$this->invokeMethod($request, 'prepareForValidation');
$this->assertFalse($request->has('password'));
}
protected function invokeMethod(&$object, $methodName, array $parameters = [])
{
$reflection = new ReflectionClass(get_class($object));
$method = $reflection->getMethod($methodName);
$method->setAccessible(true);
return $method->invokeArgs($object, $parameters);
}
}