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

Problem is that i can't test one function, because it is touching other functions of the same repository.

  1. Do I need to test one function in isolation from other functions in same repository, or it is normal that one function can access other functions in same repository ?
  2. If function needs to be tested in isolation from other, how it can be done, because I don't understand how I can mock repository in which I'm working. I understand how to mock dependencies, but how to mock other functions in same repository ?
  3. Am I mocking model correctly in setUp method in the test?

Code:

Real world binding of and repository:

// 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');
}

When running this test I got:

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

So it means that Test is trying to touch function in EloquentUser.php:

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

Do I need to mock createProfile ? because profile() cant be found. And if I need to do this, how can i do it because this function is in same repository that i'm testing?

Question is solved. Just needed to create one more Model instance and pass it in mocked method.

My Working setUp method:

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);
}

Working test method:

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');
}