简体   繁体   English

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

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

I want to use a mock object (Mockery) in my PHPUnit test.我想在我的 PHPUnit 测试中使用模拟 object (Mockery)。 The mock object needs to have both some public methods and some public properties set.模拟 object 需要同时设置一些公共方法和一些公共属性。 The class is a Laravel Eloquent model. I tried this: class 是 Laravel Eloquent model。我试过这个:

$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:此模拟 object 上不存在方法 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?当 mocking 是一个简单的 class 时不会返回此错误,但当我尝试模拟 Eloquent model 时会返回此错误。我做错了什么?

If you want getting this property with this value, just use it: 如果要使用此值获取此属性,只需使用它:

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

If you call $user->role you will get - 2 ( $user - its User mock class) 如果你调用$user->role你会得到 - 2$user - 它的User mock类)

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

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

Spy is your friend on this: 间谍是你的朋友:

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

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对象的数组,或者你更喜欢使用数组,那么至少你知道它总是你得到的数组。

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

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

You need to use a partial mock so the rest of the eloquent model will function as normal.您需要使用部分模拟,这样 eloquent model 的 rest 将 function 正常。 You're just wanting to override a certain method basically.您只是想基本上覆盖某个方法。

$this->partialMock(User::class, function ($mock) {
    $mock->shouldReceive('roles')->once();
});

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM