测试具有模型作为依赖关系的laravel存储库

时间:2013-12-20 16:27:43

标签: php unit-testing laravel mockery

问题是我无法测试一个函数,因为它正在触及同一个存储库的其他函数。

  1. 我是否需要与同一存储库中的其他函数隔离测试一个函数,或者一个函数可以访问同一个存储库中的其他函数是正常的吗?
  2. 如果函数需要与其他函数分开测试,那么它是如何完成的,因为我不明白我如何模拟我正在工作的存储库。我理解如何模拟依赖项,但如何在同一个存储库中模拟其他函数?
  3. 我是否在测试中的setUp方法中正确模拟了模型?
  4. 代码:

    真实世界绑定和存储库:

    // Bind User repository interface
    $app->bind('MyApp\Repositories\User\UserInterface', function () {
        return new EloquentUser(new User);
    });
    

    EloquentUser.php:

    public function __construct(Model $user)
    {
        $this->user = $user;
    }
    
    public function findById($id)
    {
        return $this->user->find($id);
    }
    
    public function replace($data)
    {
        $user = $this->findById($data['user']['id']);
    
        // If user not exists, create new one with defined values.
        if ( ! $user) {
            return $this->create($data);
        } else {
            return $this->update($data);
        }
    }
    
    public function create($data)
    {
        $user = $this->user->create($data['user']);
    
        if ($user) {
    
            return $this->createProfile($user, $data['profile']);
    
        } else {
    
            return false;
        }
    }
    
    private function createProfile($user, $profile)
    {
        return $user->profile()->create($profile);
    }
    
    public function update($user, $data)
    {
        foreach ($data['user'] as $key => $value) {
            $user->{$key} = $value;
        }
    
        if (isset($data['profile']) && count($data['profile']) > 0) {
    
            foreach ($data['profile'] as $key => $value) {
                $user->profile->$key = $value;
            }
        }
    
        return ($user->push()) ? $user : false;
    }
    

    EloquentUserTest.php

    public function setUp()
    {
        parent::setUp();
        $this->user = Mockery::mock('Illuminate\Database\Eloquent\Model', 'MyApp\Models\User\User');
        App::instance('MyApp\Models\User\User', $this->user);
        $this->repository = new EloquentUser($this->user);
    }
    
    public function testReplaceCallsCreateMethod()
    {
        $data = [
            'user' => [
                'id' => 1,
                'email' => 'test@test.com',
            ],
            'profile' => [
                'name' => 'John Doe',
                'image' => 'abcdef.png',
            ],
        ];
    
        // Mock the "find" call that is made in findById()
        $this->user->shouldReceive('find')->once()->andReturn(false);
    
        // Mock the "create" call that is made in create() method
        $this->user->shouldReceive('create')->once()->andReturn(true);
    
        // Run replace method that i want to test
        $result = $this->repository->replace($data);
    
        $this->assertInstanceOf('Illuminate\Database\Eloquent\Model', $result, 'Should be an instance of Illuminate\Database\Eloquent\Model');
    }
    

    运行此测试时,我得到了:

    Fatal error: Call to a member function profile() on a non-object in C:\Htdocs\at.univemba.com\uv2\app\logic\Univemba\Repositories\User\EloquentUser.php on line 107
    

    所以这意味着Test正试图触及EloquentUser.php中的函数:

    private function createProfile($user, $profile)
    {
        return $user->profile()->create($profile);
    }
    

    我需要模拟createProfile吗?因为无法找到profile()。如果我需要这样做,我该怎么做呢,因为这个函数在我正在测试的同一个存储库中?

1 个答案:

答案 0 :(得分:7)

问题解决了。 只需创建一个Model实例并以mocked方法传递它。

我的工作setUp方法:

public function setUp()
{
    parent::setUp();

    $this->user = Mockery::mock('MyApp\Models\User\User');

    App::instance('MyApp\Models\User\User', $this->user);

    $this->repository = new EloquentUser($this->user);
}

工作测试方法:

public function testReplaceCallsCreateMethod()
{
    $data = [
        'user' => [
            'id' => 1,
            'email' => 'test@test.com',
            'password' => 'plain',
        ],
        'profile' => [
            'name' => 'John Doe',
            'image' => 'abcdef.png',
        ],
    ];

    // Mock Model's find method
    $this->user->shouldReceive('find')->once()->andReturn(false);

    // Create new Model instance
    $mockedUser = Mockery::mock('MyApp\Models\User\User');

    // Mock Models profile->create and pass Model as a result of a function
    $mockedUser->shouldReceive('profile->create')->with($data['profile'])->andReturn($mockedUser);

    // Pass second instance Model as a result
    $this->user->shouldReceive('create')->once()->andReturn($mockedUser);

    // Now all $user->profile is properly mocked and will return correct data
    $result = $this->repository->replace($data);

    $this->assertInstanceOf('Illuminate\Database\Eloquent\Model', $result, 'Should be an instance of Illuminate\Database\Eloquent\Model');
}