Laravel 模拟与 Mockery Eloquent 模型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25782543/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 10:05:26  来源:igfitidea点击:

Laravel mock with Mockery Eloquent models

phplaravelmockingphpunitmockery

提问by PapaSmurf

I'm developing a PHP (5.4.25)application with laravel(4.2)framework. I'd like test my UserController with Mockery, so I've fit my UserController in this way:

我正在使用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;
        }
    }

I want mock Eloquent User model so i develop my UsersControllerTest so:

我想要模拟 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'] = '[email protected]';
        $data['password'] = 'password';
        $data['password_confirmation'] = 'password';

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

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

} 

When I run my test it returns me this error:

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

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

Why? What's wrong?

为什么?怎么了?

EDIT:I found the problem - If I don't use mock object all works fine and the controller create a new user in the DB, but when I use mock the Input:all()method returns empty array.

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

-- Thanks

- 谢谢

采纳答案by PapaSmurf

The problem was in $this->createApplication();.

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

I have commented that line and Input::all()works fine with all input parameters!

我已经评论了该行并且Input::all()可以正常使用所有输入参数!

回答by Canaan Etai

i had this same issue when i started testing..... the thing there is that, in your userscontroller store method you are actually saving the user to the database and base on your code it might work just fine but you are surprise that it is actually failing the test. Now think of it this way, you mock the user, and you told your test that when ever i call User model, please give me the mock version of my user, like you did in your code line below

我开始测试时遇到了同样的问题..... 问题是,在您的 userscontroller 存储方法中,您实际上是将用户保存到数据库中,并根据您的代码它可能工作得很好,但您很惊讶它实际上是未通过测试。现在这样想,你模拟用户,你告诉你的测试,每当我调用用户模型时,请给我我用户的模拟版本,就像你在下面的代码行中所做的那样

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

Now the problem is that you need to also call the mock version of save() method from the through the mock version of User like so:

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

$this->mock->save();

Consider the following Example:

考虑以下示例:

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);

}

i hope it helps you.

我希望它可以帮助你。

回答by Antonio Frignani

The way you are testing this, in the controller constructor is passed an instance of the real Eloquent model, not the mock. If you want to test the facades (as clearly stated in the documentation) you should call the shouldReceive()method directly on the facade to have it mocked.

您测试的方式是,在控制器构造函数中传递一个真实 Eloquent 模型的实例,而不是模拟。如果您想测试外观(如文档中明确说明的那样),您应该shouldReceive()直接在外观上调用该方法来模拟它。

But, since you are redefining the $this->uservariable in the store()method of the controller, it will not be called, unless you remove the hardcoded instantiation and use the injected $user.

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

Edit: i overlooke the $this->app->instance('User', $this->mock);line. So, the problem may be due the fact that in the storemethod you are getting a new class instance directly, and not via the Laravel IoC container. instead of $this->user = new User;in your store method, you should get it via App::make('User');

编辑:我忽略了这$this->app->instance('User', $this->mock);条线。因此,问题可能是由于在该store方法中您直接获取了一个新的类实例,而不是通过 Laravel IoC 容器。而不是$this->user = new User;在您的商店方法中,您应该通过App::make('User');