Laravel模拟与Mockery Eloquent模型

时间:2014-09-11 08:20:35

标签: php laravel mocking phpunit mockery

我正在使用 laravel(4.2)框架开发 PHP(5.4.25)应用程序。我想用 Mockery 测试我的UserController,所以我以这种方式适合我的UserController:

class UsersController extends \BaseController {
    protected $user;

    public function __construct(User $user) {
        $this->user = $user;
        $this->beforeFilter('csrf', array('on'=>'post'));
    }

    public function store() {
        $validator = Validator::make(Input::all(), User::$rules);

        if ( $validator->passes() ) {
            $this->user->username = Input::get('username');
            $this->user->password = Hash::make(Input::get('password'));
            $this->user->first_name = Input::get('first_name');
            $this->user->last_name = Input::get('last_name');
            $this->user->email = Input::get('email');
            $this->user->save();


            return true;
        } else {
            return false;
        }
    }

我想要模拟Eloquent User模型,所以我开发了我的UsersControllerTest:

class UsersControllerTest extends TestCase {

    private $mock;

    public function __construct() {}

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

        $this->createApplication();                                                     
    }

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

        Mockery::close();
    }

    public function testStore() {
        $this->mock = Mockery::mock('Eloquent','User[save]');                                           
        $this->mock
            ->shouldReceive('save')
            ->once()
            ->andReturn('true');                                                        
        $this->app->instance('User', $this->mock);                                      

        $data['username'] = 'qwerety';
        $data['first_name'] = 'asd';
        $data['last_name'] = 'asd123';
        $data['email'] = 'test@gmail.com';
        $data['password'] = 'password';
        $data['password_confirmation'] = 'password';

        $response = $this->call('POST', 'users', $data);

        var_dump($response->getContent());
    }

} 

当我运行测试时,它会返回此错误:

Mockery\Exception\InvalidCountException : Method save() from Mockery_0_User should be called
 exactly 1 times but called 0 times.

为什么呢?怎么了?

编辑:我发现问题 - 如果我不使用模拟对象一切正常,控制器在数据库中创建一个新用户,但是当我使用模拟时{{1 }}方法返回空数组。

- 感谢

3 个答案:

答案 0 :(得分:1)

你在测试它的方式,在控制器构造函数中传递一个真正的Eloquent模型的实例,而不是mock。如果你想测试外墙(如documentation中明确说明的那样),你应该直接在立面上调用shouldReceive()方法来模仿它。

但是,由于您要重新定义控制器的$this->user方法中的store()变量,因此除非您删除硬编码实例并使用注入的$user,否则不会调用它。

编辑:我覆盖了$this->app->instance('User', $this->mock);行。所以,问题可能是因为在store方法中你直接获得了一个新的类实例,而不是通过Laravel IoC容器。您的商店方法中应该使用$this->user = new User;而不是App::make('User');,而应通过{{1}}

获取

答案 1 :(得分:1)

当我开始测试时,我遇到了同样的问题..... 有一点是,在你的用户控制器存储方法中,你实际上是将用户保存到数据库中,并且根据你的代码它可能工作得很好,但你会惊讶它实际上是在测试失败。 现在想想这样,你嘲笑用户,你告诉你的测试,当我打电话给用户模型时,请给我我的用户的模拟版本,就像你在下面的代码行中所做的那样

$this->app->instance('User', $this->mock);

现在问题是你还需要通过模拟版本的User调用save()方法的模拟版本,如下所示:

$this->mock->save();

考虑以下示例:

public function testStore()
{
    $input = ['name', 'Canaan'];

    $this->mock
        ->shouldReceive('create')
        ->once()->with($input);

    $this->app->instance('User', $this->mock);
    $this->mock->create($input);

    $this->call('POST', 'users', $input);

}

我希望它可以帮到你。

答案 2 :(得分:1)

问题出在$this->createApplication();

我评论说,行和Input::all()适用于所有输入参数!