phpunit没有找到模型方法(Laravel / Mockery)

时间:2014-06-25 22:07:10

标签: laravel mockery

我开始使用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()

我错过了什么?

2 个答案:

答案 0 :(得分: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类(恰好扩展了{{} 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');      
}