Problem is that i can't test one function, because it is touching other functions of the same repository.
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');
}