Mocking Laravel Eloquent 模型 - 如何使用 Mockery 设置公共属性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22909194/
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 09:19:26  来源:igfitidea点击:

Mocking Laravel Eloquent models - how to set a public property with Mockery

phplaraveleloquentmockery

提问by mtmacdonald

I want to use a mock object (Mockery) in my PHPUnit test. The mock object needs to have both some public methods and some public properties set. The class is a Laravel Eloquent model. I tried this:

我想在我的 PHPUnit 测试中使用一个模拟对象 (Mockery)。模拟对象需要设置一些公共方法和一些公共属性。该类是 Laravel Eloquent 模型。我试过这个:

$mock = Mockery::mock('User');
$mock->shouldReceive('hasRole')->once()->andReturn(true); //works fine
$mock->roles = 2; //how to do this? currently returns an error
$this->assertTrue(someTest($mock));

... but setting the public property returns this error:

...但设置公共属性会返回此错误:

BadMethodCallException: Method Mockery_0_User::setAttribute() does not exist on this mock object

BadMethodCallException:此模拟对象上不存在方法 Mockery_0_User::setAttribute()

This error is not returned when mocking a simple class, but is returned when I try to mock an Eloquent model. What am I doing wrong?

模拟简单类时不会返回此错误,但当我尝试模拟 Eloquent 模型时会返回此错误。我究竟做错了什么?

回答by Joyful

If you want getting this property with this value, just use it:

如果您想使用此值获取此属性,只需使用它:

$mock->shouldReceive('getAttribute')
    ->with('role')
    ->andReturn(2);

If you call $user->roleyou will get - 2($user- its Usermock class)

如果你打电话,$user->role你会得到 - 2( $user- 它的User模拟类)

回答by Fancypants_MD

This answer is a bit late but hopefully it will help someone. You can currently set a static property on mocked Eloquent objects by using the 'alias' keyword:

这个答案有点晚了,但希望它会帮助某人。您目前可以使用 'alias' 关键字在模拟的 Eloquent 对象上设置静态属性:

$mocked_model = Mockery::mock('alias:Namespace\For\Model');
$mocked_model->foo = 'bar';
$this->assertEquals('bar', $mocked_model->foo);

This is also helpful for mocking external vendor classes like some of the Stripe objects.

这对于模拟外部供应商类(如某些 Stripe 对象)也很有帮助。

Read about 'alias' and 'overload' keywords: http://docs.mockery.io/en/latest/reference/startup_methods.html

阅读“别名”和“重载”关键字:http: //docs.mockery.io/en/latest/reference/startup_methods.html

回答by awei

To answer your question, you could also try something like this:

要回答您的问题,您还可以尝试以下操作:

$mock = Mockery::mock('User');
$mock->shouldReceive('hasRole')->once()->andReturn(true); //works fine
$mock->shouldReceive('setAttribute')->passthru();
$mock->roles = 2; 

$mock->shouldReceive('getAttribute')->passthru();
$this->assertEquals(2, $mock->roles);

Or, as suggested by seblaze, use a partial mock:

或者,按照 seblaze 的建议,使用部分模拟:

$mock = Mockery::mock('User[hasRole]');
$mock->shouldReceive('hasRole')->once()->andReturn(true);
$mock->roles = 2; 
$this->assertEquals(2, $mock->roles);

But, from your code snippet, if you're writing unit tests, you should really only make one assertion per each test:

但是,从你的代码片段来看,如果你正在编写单元测试,你真的应该每个测试只做一个断言:

function test_someFunctionWhichCallsHasRole_CallsHasRole() {
    $mock = Mockery::mock('User');
    $mock->shouldReceive('hasRole')->once();

    $mock->someFunctionWhichCallsHasRole();
}

function test_someFunctionWhichCallsHasRole_hasRoleReturnsTrue_ReturnsTrue() {
    $mock = Mockery::mock('User');
    $mock->shouldReceive('hasRole')->once()->andReturn(true);

    $result = $mock->someFunctionWhichCallsHasRole();

    $this->assertTrue($result);
}        

回答by Yevgeniy Afanasyev

Spyis your friend on this:

间谍是你的朋友:

$mock = Mockery::spy('User');
$mock->shouldReceive('hasRole')->once()->andReturn(true);
$mock->roles = 2;
$this->assertTrue(someTest($mock));

回答by aCiD

Partial Mock in Laravel 5.3

Laravel 5.3 中的部分模拟

$mock = Mockery::mock(App\User::class)->makePartial();
$mock->shouldReceive('hasRole')->once()->andReturn(true);
$mock->roles = 2; 
$this->assertEquals(2, $mock->roles);

回答by Oldek

Tried this? It should cover you issue.

试过这个吗?它应该涵盖你的问题。

https://github.com/padraic/mockery/blob/master/docs/11-MOCKING-PUBLIC-PROPERTIES.md

https://github.com/padraic/mockery/blob/master/docs/11-MOCKING-PUBLIC-PROPERTIES.md

I'd say implement these

我会说实施这些

protected $roles = array();

public function setRoles($roles)
{
    $this->roles = $roles;
}

public function addRole($role)
{
    $this->roles[] = $role;
}

Then you can test using:

然后您可以使用以下方法进行测试:

$mock = Mockery::mock('User');
$mock->shouldReceive('hasRole')->once()->andReturn(true);
$mock->addRole(2);
$this->assertTrue(someTest($mock));

This apse gives you the opportunity to promise a format when you do a getRoles() which would be array of Role object if you do SOLID OOP, or if you rather use array, then at least you know it's always an array you get.

这个 apse 使您有机会在执行 getRoles() 时承诺一种格式,如果您执行 SOLID OOP,它将是 Role 对象的数组,或者如果您更愿意使用数组,那么至少您知道它始终是您获得的数组。

回答by Paul Bele

Did you tried to do partial mocks? You can try something like ( NOT TESTED ) :

你试过做部分模拟吗?您可以尝试类似(未测试):

$mock = Mockery::mock('User[hasRole]');
$mock->shouldReceive('hasRole')->once()->andReturn(true);
$mock->roles = 2; 
$this->assertTrue(someTest($mock));

回答by shivanshu patel

you can use stdclass in this case.

在这种情况下,您可以使用 stdclass。

$obj = new stdClass();
$obj->roles = 2;
$mock = Mockery::mock('User');
// return stdclass here.
$mock->shouldReceive('hasRole')->once()->andReturn($obj);
$mock->roles = 2;
$this->assertEquals(2, $mock->roles);