How to Set an Expectancy on Should_Receive and Have It Execute the Original Code

Mockery shouldReceive()-once() doesn't seem to work

You need to call Mockery:close() to run verifications for your expectations. It also handles the cleanup of the mockery container for the next testcase.

public function tearDown()
{
parent::tearDown();
m::close();
}

Set up multiple return values with Mockery and hard dependency

Thanks for your question.

You might have come into troubled waters by applying two (new?) things at once and then falling between the two.

You should actually see your test fail. And then go with the failure to locate any problems with the expectations.

That does require writing the test step by step for the expectations you have for the testing framework and the mocking library in use before you can use that again to formulate your expectations. Otherwise things can easily grow out of proportions and then it can easily become a "not seeing the wood for the trees" situation.

return (new Greeting())->welcome();

You can then answer the question whether or not you have misread something in the documentation yourself as well. That is the essence of a test and how you should enable yourself to make use of a testing framework - otherwise it's not worth for your needs.


Next to this introductory, more general comment, there are multiple errors in your code that prevent effective mocking and testing. Lets take a look, maybe by going through some potential misunderstandings can be cleared up as well:

$greetingMock->shouldReceive('get')

The method name is welcome not get. You should have seen an error about that:

Mockery\Exception\BadMethodCallException : Method App\Greeting::welcome() does not exist on this mock object

With a working example though you may learn that the expectations configured Mockery are not effective by default. Which would be easy to find out by turning twice() into a never() and seeing that calling $foo->greeting() does not make the test fail.

Again this might be just an incomplete example in the question but this remains unknown to me then. So double check you have expectations "online" on your end as otherwise you would not know that twice() would work or not.

Why do I put the focus on twice()?

Well because as you shared with your question you already found out that

andReturn(A, B)

only returns A, which according to the documentation means it is the first time the method is called.

With expectations enabled, this would be revealed directly:

Mockery\Exception\InvalidCountException : Method welcome() from App\Greeting should be called exactly 2 times but called 1 times.

So perhaps expectations in general are not working on your end yet? So perhaps double check you have your declarations active. See the note at the very top of Mockery Expectation Declarations.

With expectations enabled it may become obvious that the method is not called twice but only once and it therefore correctly applies with addReturn returning for the first call, only.

Mockery\Exception\InvalidCountException : Method welcome() from App\Greeting should be called exactly 2 times but called 1 times.

Now a back to your asking in the original question:

Am I misreading the documentation about the andReturn method?

No, you're reading it fine.

Its just that your expectation that the method would be called twice is wrong.

It is called only once. This is written in your example code as well:

return (new Greeting())->welcome();

On each (new) Greeting, welcome is called once - not twice.

Enforcing a test to fail first by using never instead of twice for example is an easy technique to the rough edges out of "non-working test" fast.

You might have learned that as a kid while playing:

If something works, try to break it.

In testing, break things first. You actually want to show what the boundaries of "working" are.


Therefore if a test fails and it is unclear why it fails, the test is not of much use.

For unit-tests it is said specifically that the test is also badly written, because there should be only one reason a single test fails, so that it is clear when it is getting red what the issue is.

A test like the test in question was far away from ready. Some expectations were formulated, but never asserted.

Putting a simplified and working example into a question here on Stackoverflow may already help with that.

Instead you created intermediate code within a test-method to test for something differently, with expectations just as comments not asserting automatically.

Next time, just isolate what you'd like to test in a far smaller, own test-method first. Get that test done first then, then continue with the other test-method.

Document your own understanding incl. your preconditions to have a test work. Its your test. And it's easier to start small and then change instead of writing the whole test upfront.

Mock should be called exactly 1 times but called 0 times

Looking at your test case:

public function testSendEmailToNewUserListener()
{
$user = factory(MCC\Models\Users\User::class)->create();

Mail::shouldReceive('send')
->with(
'emails.register',
['user' => $user],
function ($mail) use ($user) {
$mail->to($user->email, $user->name)
->subject('Thank you for registering an account.');
}
)
->times(1)
->andReturnUsing(function ($message) use ($user) {
dd($message);
$this->assertEquals('Thank you for registering an account.', $message->getSubject());
$this->assertEquals('mcc', $message->getTo());
$this->assertEquals(View::make('emails.register'), $message->getBody());
});
}

Either:

  • Creating the user calls the Mail facade, in which case you're calling that facade before you're mocking it up.

OR

  • You aren't calling the function which calls the Mail facade.

Either way, Mail::shouldReceive('send') should not be the last thing in the test case.

The error you are getting is because Mail is expecting the call to happen after you call ::shouldRecieve(), but it's not - the test case ends, and the Mockery instance has never been called.

You could try something like this instead:

public function testSendEmailToNewUserListener()
{
$testCase = $this;

Mail::shouldReceive('send')
->times(1)
->andReturnUsing(function ($message) use ($testCase) {
$testCase->assertEquals('Thank you for registering an account.', $message->getSubject());
$testCase->assertEquals('mcc', $message->getTo());
$testCase->assertEquals(View::make('emails.register'), $message->getBody());
});

$user = factory(MCC\Models\Users\User::class)->create();
}

Can I combine shouldReceive and shouldNotReceive in Mockery for a Controller Test in ZF2 with Doctrine Entity Manager?

You can try something like this. The idea is to first setup a default expectation using byDefault() and than define your specific expectations, which will be preferred over the default ones.

$entityManager
->shouldReceive('getRepository')
->with(\Mockery::any())
->andReturn(\Mockery::mock('Doctrine\ORM\EntityRepository'))
->byDefault();

$entityManager
->shouldReceive('getRepository')
->with('App\Entity\Product')
->andReturn(\Mockery::mock('App\Repository\Product'));

Set especific Mockery expectation only when needed

After asking in the #mockery IRC channel, I got this answer, and it works:ç

Instead of using plain ->andReturn(), ->andReturnUsing() (which accepts a closure) can be used, like this:

->andReturnUsing(function ($attr) {
if ($attr == 'pedidoline') {
return new \Illuminate\Support\Collection();
}
return 1;
});

makePartial() returns Mockery\Exception\BadMethodCallException : Method does not exist on this mock object

Delete :overload and just define your mock as:

$mockCategory = \Mockery::mock(Category::class)->makePartial()

Example

Model:

namespace App\Models;

class Foobar extends BaseModel
{
public function foonction()
{
Foobar::find();
return '1';
}
}

Test:

namespace Tests;

use Evalua\Heva\Models\Foobar;

class FoobarTest extends TestCase
{
public function testFoobar()
{
$fooMock = \Mockery::mock('overload:'.Foobar::class)->makePartial();
$fooMock->shouldReceive('find')->once();
$fooMock->foonction();
}
}

Fails with:

Mockery\Exception\BadMethodCallException: Method Evalua\Heva\Models\Foobar::foonction() does not exist on this mock object

Without the :overload the test pass.

The explanation should be based on what's written in the documentation about overload:

Prefixing the valid name of a class (which is NOT currently loaded) with “overload:” will generate an alias mock (as with “alias:”) except that created new instances of that class will import any expectations set on the origin mock ($mock). The origin mock is never verified since it’s used an expectation store for new instances. For this purpose we use the term “instance mock”

Setting up a Mockery's Expectations in relation to protected methods

It turns out my suspicion that it was related to the fact that two different MyEvent instances were being created was correct. Unfortunately, it means the title to my question is misleading. After digging, I found out that my code worked fine once I changed my Expectations() to the following.

@Test
public void sourceShouldThrowEventOnEventOccurrence() {
// set up
Mockery context = new Mockery();
MyEventListener listener = context.mock(MyEventListener.class);
MySource source = new MySource();
// MyEvent event = new MyEvent(source); <-- NO LONGER NEEDED
source.addListener(listener);

// set expectations
context.checking(new Expectations() {{
oneOf(listener).handleMyEvent(with(any(MyEvent.class))); // <-- with any
}});

// execute
// do stuff to (theoretically) raise event

// verify
context.assertIsSatisfied();
}


Related Topics



Leave a reply



Submit