0

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

我一直在通过直接调用控制器方法(以单独对方法进行单元测试)和通过路由调用方法(以专注于响应)进行测试,但根据我是否调用控制器与走路线。

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

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()方法设置的,以确保它们是一致的。

然而,当它从我的控制器中的方法抛出 ResourceNotFoundException 时testDestroyOk()通过并testDestroyOkRoute()失败。getUser()

任何想法为什么访问控制器有效但通过路由以某种方式被不同地对待?

4

1 回答 1

0

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

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

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");  
}
于 2014-07-03T08:29:32.193 回答