31

我想在我的 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));

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

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

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

4

8 回答 8

24

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

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

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

于 2014-10-24T20:02:53.060 回答
8

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

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

这也有助于模拟像一些 Stripe 对象这样的外部供应商类。

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

于 2015-08-21T22:23:25.380 回答
3

要回答您的问题,您也可以尝试以下方法:

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

或者,正如 seblaze 所建议的,使用部分模拟:

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

但是,从您的代码片段来看,如果您正在编写单元测试,那么每个测试实际上应该只做一个断言:

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);
}        
于 2014-04-18T22:49:37.700 回答
1

间谍是你的朋友:

$mock = Mockery::spy('User');
$mock->shouldReceive('hasRole')->once()->andReturn(true);
$mock->roles = 2;
$this->assertTrue(someTest($mock));
于 2017-09-15T04:55:07.710 回答
0

您是否尝试过部分模拟?您可以尝试类似(未测试):

$mock = Mockery::mock('User[hasRole]');
$mock->shouldReceive('hasRole')->once()->andReturn(true);
$mock->roles = 2; 
$this->assertTrue(someTest($mock));
于 2014-04-08T07:39:11.997 回答
0

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

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

我会说实施这些

protected $roles = array();

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

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

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

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

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

于 2014-04-07T10:03:46.153 回答
0

Laravel 5.3 中的部分模拟

$mock = Mockery::mock(App\User::class)->makePartial();
$mock->shouldReceive('hasRole')->once()->andReturn(true);
$mock->roles = 2; 
$this->assertEquals(2, $mock->roles);
于 2018-09-06T17:19:57.720 回答
-3

在这种情况下,您可以使用 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);
于 2016-09-12T15:19:48.803 回答