1

In Laravel 5.2, I want to unit test my Eloquent User Repository.

class EloquentUserRepository implements UserRepositoryInterface
{
    private $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function oneUser($id)
    {
        return $this->user->oneUser($id);
    }
}

My test looks like below, with mocking the interface:

class EloquentUserRepositoryTest extends TestCase
{
    public function setUp()
    {
        $this->user = factory(User::class, 1)->create(['name' => 'foo']);
    }

    /** @test */
    public function it_fetch_an_user()
    {

        $mock = Mockery::mock('App\Repositories\Interfaces\UserRepositoryInterface')
        ->shouldReceive('oneUser')
        ->once()
        ->with($this->user->id)
        ->andReturn('foo');

        App::instance(App\Repositories\EloquentUserRepository::class, $mock);
        $userRepository = App::make(App\Repositories\EloquentUserRepository::class);

        $this->assertEquals('foo', $userRepository->oneUser($this->user->id)->name);

    }

    public function tearDown()
    {
        Mockery::close();
    }
}

I get this error:

ErrorException: call_user_func_array() expects parameter 1 to be a valid callback, class 'Mockery\Expectation' does not have a method 'oneUser'

I expect a simulated object that has the method oneUser, but it returns Mockery\Expectation. What do I wrong?

1 Answer 1

4

When a new instance of EloquentUserRepository is made, a new user model is created. When you then call the oneUser method for the EloquentUserRepository class a method with the same name is called but on the user model. Therefore it's the user model you need to mock, not the UserRepositoryInterface.

You need to create a new instance of the EloquentUserRepository and send in the user model mock as an argument when it's created as shown below:

class EloquentUserRepositoryTest extends TestCase
{
    protected $userMock;

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

        $this->userMock = Mockery::mock('User');
        $this->userMock->id = 1;
    }

    /** @test */
    public function it_fetch_an_user()
    {
        $this->userMock->shouldReceive('oneUser')->with($this->userMock->id)->andReturn('foo');

        $userRepository = App::make(App\Repositories\EloquentUserRepository::class, array($this->userMock));

        $this->assertEquals('foo', $userRepository->oneUser($this->userMock->id));
    }

    public function tearDown()
    {
        Mockery::close();
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Just a comment, I doubt this is the right answer. When you mock the model User, it means that for example one day if you want to switch out Eloquent; you will need to refactor your test and remove the model User with something like MongoUser. This contradicts with the repository pattern. My understanding is that you shouldn't have to refactor your tests when switching the implementation of an interface.
As an addition to my previous comment. I have scoured the web and smart people tend to suggest that we should not test repositories. We should rather write integration tests to test if our repository calls work. I think they have a point. Please see url url https://laracasts.com/discuss/channels/testing/phpunit-testing-repositories Good luck. Please also remember: Brilliance is evenly distributed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.