【问题标题】:phpunit not finding model method (Laravel / Mockery)phpunit 找不到模型方法(Laravel / Mockery)
【发布时间】:2014-08-16 15:36:13
【问题描述】:

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

我错过了什么?

【问题讨论】:

    标签: laravel mockery


    【解决方案1】:

    这里有两个问题:

    首先,您的 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...
    

    因为User 继承自BaseModel,所以BaseModel 中的方法实际上是User 上的方法,可以将它们视为定义为User 的一部分。

    【讨论】:

    • 太好了 - 非常感谢。将 logAndStream 更改为 public 允许测试现在按预期访问它。您建议如何测试在同一个用户对象上调用 logIt 和 streamIt(假设 sholdReceive 不适用于模型)?
    • 我在模拟模型上使用 shouldReceive 从来没有遇到过问题。我将编辑答案以添加简短的 sn-p。
    • 非常感谢。我现在已经糊涂了,将发布最终代码。顺便说一句,我对 shouldReceive 的引用是在(真实的)Eloquent 模型上,而不是 Mockery 模型上。我不认为我可以从真正的数据库中提取一些东西并测试应该接收。如果有办法做到这一点,那么我有兴趣学习。
    • 啊,我明白了。我认为你是对的——我认为这是不可能的。虽然如果你使用repository,你可能能够完成类似的事情。
    【解决方案2】:

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

    首先,我应该检查 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');      
    }
    

    【讨论】:

      猜你喜欢
      • 2013-03-24
      • 2014-08-24
      • 2016-09-17
      • 2015-11-15
      • 2022-01-04
      • 2016-07-22
      • 2014-11-05
      • 2013-05-24
      • 2016-01-28
      相关资源
      最近更新 更多