Laravel - 如何使用 mockery 对更新用户数据的中间件进行单元测试
Laravel - How to unit test a middleware updating user's data using mockery
我有以下中间件来更新用户的 last_seen_at
字段,我只将它用于经过身份验证的用户可以调用它的路由:
// app/Http/Middleware/LastSeen.php
public function handle($request, Closure $next)
{
$user = $request->user();
$user->last_seen_at = now();
$user->save();
return $next($request);
}
// app/Http/Kernel.php
protected $routeMiddleware = [
// other middleware
'lastseen' => \App\Http\Middleware\LastSeen::class
]
// routes/api.php
Route::group([
'middleware' => ['jwt', 'lastseen']
], function () {
// routes
});
我编写了以下用于测试中间件的单元测试:
// tests/Unit/LastSeenMiddlewareTest.php
/** @test */
public function doesLastSeenMiddlewareUpdateUsersLastSeenAt()
{
$user = Mockery::mock(User::class);
$user->shouldReceive('setAttribute')->passthru();
$user->shouldReceive('save')->once();
$user = $user->makePartial();
app()->instance(User::class, $user);
$request = Mockery::mock(Request::class)
->shouldReceive('user')
->once()
->andReturn()
->getMock();
$middleware = new LastSeen;
$middleware->handle($request, function () {});
}
通过运行测试,我得到一个错误:ErrorException: Creating default object from empty value
。但是,我不明白能够解决它的问题。
好吧,这个问题从一开始就很愚蠢。我错过了将 $user
作为模拟 Request
的 user
方法的 return 值传递。以下测试通过:
// tests/Unit/LastSeenMiddlewareTest.php
/** @test */
public function doesLastSeenMiddlewareUpdateUsersLastSeenAt()
{
$user = Mockery::mock(User::class);
/* As I'm making a partial mock,
I don't need to worry about passing
through the call to the setAttribute method */
$user->shouldReceive('save')->once();
$user = $user->makePartial();
app()->instance(User::class, $user);
$request = Mockery::mock(Request::class)
->shouldReceive('user')
->once()
->andReturn($user) // I should've passed $user here
->getMock();
$middleware = new LastSeen;
$middleware->handle($request, function () {});
$this->assertEqualsWithDelta($user->last_seen_at, now(), 1);
}
归根结底,middlaware 只是一个带有 handle
方法的经典 class
。
我将这样进行单元测试:
public function doesLastSeenMiddlewareUpdateUsersLastSeenAt()
{
//note: this is using the old factory syntax prior to Laravel 8 https://github.com/laravel/legacy-factories
$user = factory(User::class)->create(['last_seen_at' => now()->subHour()]);
//login as the user since in your middleware you get the logged user
$this->actingAs($user);
//get the middleware through the container
$middleware = app(LastSeen::class);
//call handle method
$middleware->handle(request(), function () {});
$this->assertEqualsWithDelta($user->refresh()->last_seen_at, now(), 1);
}
我有以下中间件来更新用户的 last_seen_at
字段,我只将它用于经过身份验证的用户可以调用它的路由:
// app/Http/Middleware/LastSeen.php
public function handle($request, Closure $next)
{
$user = $request->user();
$user->last_seen_at = now();
$user->save();
return $next($request);
}
// app/Http/Kernel.php
protected $routeMiddleware = [
// other middleware
'lastseen' => \App\Http\Middleware\LastSeen::class
]
// routes/api.php
Route::group([
'middleware' => ['jwt', 'lastseen']
], function () {
// routes
});
我编写了以下用于测试中间件的单元测试:
// tests/Unit/LastSeenMiddlewareTest.php
/** @test */
public function doesLastSeenMiddlewareUpdateUsersLastSeenAt()
{
$user = Mockery::mock(User::class);
$user->shouldReceive('setAttribute')->passthru();
$user->shouldReceive('save')->once();
$user = $user->makePartial();
app()->instance(User::class, $user);
$request = Mockery::mock(Request::class)
->shouldReceive('user')
->once()
->andReturn()
->getMock();
$middleware = new LastSeen;
$middleware->handle($request, function () {});
}
通过运行测试,我得到一个错误:ErrorException: Creating default object from empty value
。但是,我不明白能够解决它的问题。
好吧,这个问题从一开始就很愚蠢。我错过了将 $user
作为模拟 Request
的 user
方法的 return 值传递。以下测试通过:
// tests/Unit/LastSeenMiddlewareTest.php
/** @test */
public function doesLastSeenMiddlewareUpdateUsersLastSeenAt()
{
$user = Mockery::mock(User::class);
/* As I'm making a partial mock,
I don't need to worry about passing
through the call to the setAttribute method */
$user->shouldReceive('save')->once();
$user = $user->makePartial();
app()->instance(User::class, $user);
$request = Mockery::mock(Request::class)
->shouldReceive('user')
->once()
->andReturn($user) // I should've passed $user here
->getMock();
$middleware = new LastSeen;
$middleware->handle($request, function () {});
$this->assertEqualsWithDelta($user->last_seen_at, now(), 1);
}
归根结底,middlaware 只是一个带有 handle
方法的经典 class
。
我将这样进行单元测试:
public function doesLastSeenMiddlewareUpdateUsersLastSeenAt()
{
//note: this is using the old factory syntax prior to Laravel 8 https://github.com/laravel/legacy-factories
$user = factory(User::class)->create(['last_seen_at' => now()->subHour()]);
//login as the user since in your middleware you get the logged user
$this->actingAs($user);
//get the middleware through the container
$middleware = app(LastSeen::class);
//call handle method
$middleware->handle(request(), function () {});
$this->assertEqualsWithDelta($user->refresh()->last_seen_at, now(), 1);
}