2014-06-25 69 views
0

我开始在Laravel 4中进行单元测试,并且在我添加到标准User模型的模型中卡住测试自定义方法。phpunit找不到模型方法(Laravel/Mockery)

use Illuminate\Auth\UserInterface; 
use Illuminate\Auth\Reminders\RemindableInterface; 

class User extends BaseModel implements UserInterface, RemindableInterface { 

    /** 
    * Logs to the database and streams an update 
    * Uses logIt and streamIt on Base model 
    * @param String $action The action being performed 
    * @return void   
    */  
    private function logAndStream($action) 
    { 
     $this->logIt('info', $action.'d user '.$this->username); 
     $this->streamIt($action.'d user '.$this->username);   
    } 

此类扩展这又延伸口才的BaseModel,并具有定义Logit模型和StreamIt方法,像这样:

class BaseModel extends Eloquent { 

/** 
* Log an action to log file 
* @return void 
*/ 
protected function logIt($level, $msg) { 
    ... 
} 

/** 
* Log an action to activity stream 
* @return void 
*/ 
protected function streamIt($msg, $client = null, $project = null) { 
    ... 
} 

所有这些代码工作正常,当我手动测试的东西。但是现在我想创建一个单元测试来自动化它。

class UserTest extends TestCase { 

public function testLogAndStream() 
{ 
    $base = Mockery::mock('BaseModel')->shouldAllowMockingProtectedMethods(); 
    $base->shouldReceive('logIt') 
       ->with('info', 'Created user Tester') 
       ->once(); 

    $user = new User; 
    $user->username = 'Tester'; 
    $user->logAndStream('Create'); 
} 

当我尝试运行这个,我得到一个失败抱怨找不到logAndStream。

1) UserTest::testLogAndStream 
BadMethodCallException: Call to undefined method Illuminate\Database\Query\Builder::logAndStream() 

我错过了什么?

回答

0

您这里有两个问题:

首先,你User模型包含logAndStream方法,但是它是私有的。这意味着这样做:

$user->logAndStream('Create'); 

是不可能的,因为只有公共方法可以通过这种方式访问​​。

第二个,在你的测试中,你正在嘲笑BaseModel的一个实例。这与您稍后实例化几行后的User实例无关。 User延伸BaseModel - 您仍然可以拥有UserBaseModel的实例,这些实例彼此不相关。这里有一个比喻:

class Database { 
} 

class DatabaseWithExtraFeatures extends Database { 
} 

第一个(Database)只是一个普通的旧数据库访问类作品就好了基本的东西。然后有人出现并意识到Database没有提供某些额外的功能,所以他们通过扩展它来构建它。开发人员可以在同一个应用程序中使用,或者甚至两者都使用。

$db = new Database; 

$dbExtra = new DatabaseWithExtraFeatures; 

// do something with $db 
$result1 = $db->query(); 

// do something with $dbExtra 
$result2 = $dbExtra->extraSpecialQuery(); 

您在您的测试做了什么类似于这样 - 你嘲笑的BaseModel一个实例,然后实例化一个User类(这恰好延长BaseModel)。

编辑下面是关于如何嘲笑用户模型更详细一点:

$user = Mockery::mock('User')->shouldAllowMockingProtectedMethods(); 

$user->shouldReceive('logIt') 
      ->with('some arguments') 
      ->once(); 

$user->shouldReceive('streamIt') 
      ->with('some arguments') 
      ->once(); 

// when you set a property on an Eloquent model, it actually calls the 
// setAttribute method. So do this instead of $user->username = 'Tester' 
// 
$user->shouldReceive('setAttribute') 
    ->with('username', 'Tester') 
    ->once() 

$user->logAndStream('Create'); 

// now assert something... 

因为UserBaseModel继承,从BaseModel的方法实际上是对User方法和好像他们是可以治疗定义为User的一部分。

+0

大 - 非常感谢你。将logAndStream更改为public允许测试现在按预期访问它。你会如何建议测试logIt和streamIt在同一个User对象上调用(因为sholdReceive不适用于Models)? – Fixspec

+0

我从来没有在模拟模型上使用'shouldReceive'出现问题。我将编辑答案以添加一个简短的片段。 – Kryten

+0

非常感谢。我现在已经弄糊涂了,会发布最终代码。顺便说一句,我参考shouldReceive是(真正的)雄辩模型,而不是嘲笑模型。我不认为我可以从真实数据库中抽出一些东西并测试shouldReceive。如果有办法做到这一点,那么我有兴趣学习。 – Fixspec

0

免责声明:这是从问题中提取的。

首先,我应该检查logIt和streamIt在相同的,模拟的用户模型而不是父级BaseModel上观察到的地方。

$user->username填充模拟用户模型也不正确。最后,Kryten帮我意识到Eloquent内部调用了getAttribute('username'),所以我可以直接返回值作为断言的一部分,然后将其输入到logIt和StreamIt中。这有效,但感觉有点笨重 - 如果有人可以提出更好的方法,我很乐意学习。

这里的工作测试用例其作品logAndStream是否被声明为不论公立或保护:

public function testLogAndStream() 
{ 
$user = Mockery::mock('User'); 

$user->shouldReceive('getAttribute') 
    ->with('username') 
    ->atLeast()->once() 
    ->andReturn('Tester'); 

$user->shouldReceive('logIt') 
    ->once() 
    ->with('info','Created user Tester'); 

$user->shouldReceive('streamIt') 
    ->once() 
    ->with('Created user Tester'); 

$user->logAndStream('Create');  
}