我开始使用Laravel 4中的单元测试,并且我已经在我已经添加到标准用户模型的模型中测试自定义方法。
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,后者又扩展了Eloquent,并且定义了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 :(得分:0)
这里有两个问题:
首先,您的User
模型包含logAndStream
方法,但它是私有的。这意味着这样做:
$user->logAndStream('Create');
是不可能的,因为只有这样才能访问公共方法。
第二次,在您的测试中,您正在模拟BaseModel
的实例。这与稍后实例化几行的User
实例无关。 User
扩展 BaseModel
- 您仍然可以拥有彼此无关的User
和BaseModel
实例。这是一个类比:
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
类(恰好扩展了{{} 1}})。
编辑以下是有关如何模拟用户模型的详细信息:
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...
继承自User
,因此BaseModel
中的方法实际上是BaseModel
上的方法,可以将其视为User
的一部分}。
答案 1 :(得分:0)
免责声明:这是从问题中提取的。
首先,我应该检查logIt和streamIt在同一个模拟的User模型上观察到的情况,而不是父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');
}