Laravel控制器为路由测试提供不同的答案

时间:2014-07-02 10:29:52

标签: laravel mockery

我正在使用Laravel 4和Mockery为控制器构建一些单元测试。

我一直在测试直接调用控制器方法(单独测试方法),并通过路由调用方法(专注于响应),但我得到了不同的答案我是否打电话给控制器vs通过路线。

这是我足智多谋的控制器:

class UserController extends \BaseController {

    protected $user;
public function __construct(User $user)
{
    $this->user = $user;
}

/**
 * Remove the specified resource from storage.
 *
 * @param  int  $id
 */
public function destroy($id)
{
    // Get and delete user
    $this->getUser($id);
    $this->user->delete();

    return $this->ok();             // This is just a json response::json
}

/**
 * Attempts to find the user requested 
 * 
 * @param  $id The ID they are trying to find
 */
private function getUser($id) 
{
    // Attempt to find the user
    $this->user = $this->user->find($id);

    // Throw exception if we can't find it
    if(is_null($this->user)) throw new ResourceNotFoundException('User '.$id.' not found'); 

    return;
}

这是我的路线:

Route::resource('users', 'UserController', array('only' => array('index', 'store','show','update','destroy')));

以下是我的测试:

use Way\Tests\Factory;

class UserControllerUnitTest extends TestCase {

public function setUp() 
{
    parent::setUp();

    // Fake objects
    $this->fake_user_nonadmin = Factory::user(array('id' => 2, 'admin' => FALSE, 'deleted_at' => null));

    // Mock objects
    $this->mock_user = Mockery::mock('User')->makePartial();  

    // Make the controller
    $this->controller = new UserController($this->mock_user);
}
public function tearDown()
{
    Mockery::close();
}

protected function prepDestroyOk($id) 
{
    $this->mock_user
         ->shouldReceive('find')
         ->once()
         ->with($id)
         ->andReturn($this->mock_user);

    $this->mock_user
         ->shouldReceive('delete')
         ->once()
         ->andReturn('foo');
}
public function testDestroyOk() 
{
    $id = $this->fake_user_nonadmin->id;
    $this->prepDestroyOk($id);

    $this->controller->destroy($id);
}       
public function testDestroyOkRoute() 
{
    $id = $this->fake_user_nonadmin->id;
    $this->prepDestroyOk($id);

    $response = $this->client->request('DELETE', 'users/'.$id);
    $this->assertResponseOk();
    $this->assertEquals(get_class($response), "Illuminate\Http\JsonResponse");
}

您看到我正在testDestroyOk()测试直接控制器访问权限,而不是通过routes.php为testDestroyOkRoute()调用相同的方法。两个测试用例都使用通用prepDestroyOk()方法设置,以确保它们一致。

然而testDestroyOk()传递并且testDestroyOkRoute()失败,因为它从我的控制器中的getUser()方法抛出ResourceNotFoundException。

为什么访问控制器工作但通过路线的任何想法都以某种方式区别对待?

1 个答案:

答案 0 :(得分:0)

我终于意识到我忘记将模拟User对象绑定到IoC容器中,这就是测试失败的原因。直接调用控制器时,模拟User对象是测试类的一部分。当通过路由调用时,Laravel正在解析一个(非模拟的)User对象,导致它失败。

将测试更新为以下内容使其按预期传递:

public function testDestroyOkRoute() 
{
    $id = $this->fake_user_nonadmin->id;
    $this->prepDestroyOk($id);
    $this->app->instance('User',$this->mock_user);   // <= I was forgetting this

    $response = $this->call('DELETE', 'users/'.$id);
    $this->assertResponseOk();
    $this->assertEquals(get_class($response), "Illuminate\Http\JsonResponse");  
}