Skip to content

Instantly share code, notes, and snippets.

@JeffreyWay
Last active March 1, 2020 07:14
Show Gist options
  • Save JeffreyWay/4968363 to your computer and use it in GitHub Desktop.
Save JeffreyWay/4968363 to your computer and use it in GitHub Desktop.
To make for clean and readable tests, do your mocking in a base model that your Eloquent models extend.
<?php
class BaseModel extends Eloquent {
public static function shouldReceive()
{
$repo = get_called_class() . 'RepositoryInterface';
$mock = Mockery::mock($repo);
App::instance($repo, $mock);
return call_user_func_array(array($mock, 'shouldReceive'), func_get_args());
}
}
<?php
class PostsTest extends TestCase {
public function testAllPosts()
{
// This will mock the PostRepository interface, update the instance
// that will be injected into the controller (use DI), and stub the getAll method.
// This way, the DB will never be hit.
Post::shouldReceive('getAll')->andReturn('foo');
// Call the route.
$response = $this->call('GET', 'posts');
// Make sure that the request was successful.
$this->assertTrue($response->isOk());
// also make sure that $posts is bound to the view
$this->assertEquals('foo', $response->getOriginalContent()->posts);
}
}
@JeffreyWay
Copy link
Author

This could all easily be done within your base TestCase class -- but it lessens readability considerably.

I'm going to talk about it at my Laracon presentation this week, and ask for thoughts.

@danharper
Copy link

@karptonite How are you testing your EloquentFooRepository and mocking the injected Foo?

eg.:

<?php
class EloquentFooRepository implements FooRepositoryInterface {
  public function __construct(Foo $foo) {
    $this->foo = $foo;
  }

  public function all() {
    return $this->foo->all();
  }
}

// ...

class TestEloquentFooRepository extends TestCase {
  public function testAll() {
    $mock = m::mock('Foo')
      ->shouldReceive('all')->once()
      ->andReturn(array());

    App::instance('Foo', $mock);

    $repo = App::make('EloquentFooRepository');
    var_export($repo->all());
  }
}

Throws:

Starting test 'TestEloquentFooRepository::testAll'.
PHP Fatal error:  Using $this when not in object context in /Library/WebServer/Sites/jobs2/vendor/mockery/mockery/library/Mockery/Generator.php(129) : eval()'d code on line 119

I'm sure I'm missing something with the mock...

Calling it in the controller works and returns the correct DB data:

<?php

class FooController extends BaseController {
  public function __construct(FooRepositoryInterface $foo)
  {
    $this->foo = $foo;
  }

  public function index()
  {
    echo '<pre>';
    print_r($this->foo->all());
  }
}

// in routes:
App::bind('FooRepositoryInterface', 'EloquentFooRepository');

Edit: I'm now just using a sqlite db in memory instead of mocking it.

@karptonite
Copy link

@danharper Just saw your question. My Repository tests also use sqlite in memory, rather than mocking the Foo. I get the same bug as you do when I try to mock Foo. I'm not certain, but I think that the problem is that all() is a static method.

@karptonite
Copy link

@danharper in the framework tests, they extend the Eloquent class with a stub, to replace the newQuery method. IT is a bit convoluted, but it might work:
https://github.com/laravel/framework/blob/master/tests/Database/DatabaseEloquentModelTest.php

@danharper
Copy link

@karptonite Ah thanks, I'll give it a try! Only just noticed your reply, guess GitHub doesn't create notifications on mentions in Gists.

@andheiberg
Copy link

I had trouble using this snippet. I've found the problem to be namespaces. I thought I would share my modified code.

My repositories are in the Repositories namespace and models are in the Models namespace.

public static function shouldReceive($args)
{
    $calledClassParts = explode('\\', get_called_class());
    $calledClass = end($calledClassParts);

    $repo = 'Repositories\\' . $calledClass . 'RepositoryInterface';
    $mock = Mockery::mock($repo);

    App::instance($repo, $mock);

    return call_user_func_array([$mock, 'shouldReceive'], func_get_args());
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment