Testing chained method call in Mockery Testing chained method call in Mockery laravel laravel

Testing chained method call in Mockery


Originally a comment but moved to answer to make the code legible!

I lean toward @alexrussell's answer too, though a middle ground would be:

$this->mock->shouldReceive('remember->with->notSold->take->orderBy->get')    ->andRe‌​turn($this->collection);


I'm quite new to testing myself, and this whole answer may be wrong in most people's eyes, but I do see a prevalence of people testing the wrong thing. If you test exactly everything a method does then you're not testing, but just writing a method twice.

You should think of your code as something of a black box - don't presume to know what's going on inside when you write your tests. Call a method with a given input, expect an output. Sometimes you need to ensure that certain other effects have happened, and that's when the shouldReceive stuff comes in. But again it's more high level than this collection chain testing - you should be testing that the code to do what this code does is done, but exactly that the code itself happens. As such, the collection chain should be extracted to some other method somehow, and you should simply test that that method is called.

The more you're testing the actual written code (rather than the purpose of the code) the more problems you will have. For example, if you need to update the code to do the same thing a different way (maybe remember(6) not remember(5) as part of that chain or something), you also have to update your test to ensure that remember(6) is now called, when you shouldn't be testing that at all.

This advice doesn't just go for chained methods of course, it's for any time you ensure that various objects have various methods called on them when testing a given method.

As much as I dislike the term 'red, green, refactor' you should consider it here as there are two points where your testing method is failing:

  • Red/Green: When you first write the failing test, your code shouldn't have all these shouldReceives (maybe one or two if it makes sense to, see above) - if it does, then you're not writing a test but you're writing the code. And really, it's an indication that you wrote the code first then the test to fit the code, which is against test-first TDD.
  • refactor: Assuming you have written the code first, then the test to fit the code (or hey somehow managed to guess exactly what shouldReceives to write in your test that the code just magically worked out). That's bad, but let's say you did it, as it's not the end of the world. You now need to refactor, but you can't without changing your test. You test is so closely coupled to the code, that any refactoring will break the test. That is, again, against the idea of TDD.

Even if you don't follow test-first TDD, you should at least realise that the refactor step should be doable without breaking your tests.

Anyway, that's just my tuppence.


I've discovered this technique, but I don't love it. It's very verbose. I think there must be a cleaner/simpler method to achieving this.

In the constructor:

$this->collection = Mockery::mock('Illuminate\Database\Eloquent\Collection')->shouldDeferMissing();

In the test:

$this->mock->shouldReceive('remember')->with(5)->andReturn($this->mock);$this->mock->shouldReceive('with')->with('user')->andReturn($this->mock);$this->mock->shouldReceive('notSold')->andReturn($this->mock);$this->mock->shouldReceive('take')->with(25)->andReturn($this->mock);$this->mock->shouldReceive('orderBy')->with('created_at', 'DESC')->andReturn($this->mock);$this->mock->shouldReceive('get')->andReturn($this->collection);