bwrigley's avatar

Using Mockery, but original method still being called

I'm sure I'm missing something really obvious here as I have mocks with Mockery working elsewhere in my feature tests.

This is what I have in a test currently:

        $mock = Mockery::mock(IntercomGateway::class)->makePartial();
        $mock->shouldReceive('createUser')->andReturn('mocked');
        $this->app->instance(IntercomGateway::class, $mock);

        $gw = new IntercomGateway;

        dump($gw->createUser(new \App\User));

I have also added this line to the original createUser() :

dump('Original method executed');

When I run the test I see Original method executed

Any thoughts on what I might have missed? Thank you

0 likes
13 replies
Talinon's avatar

@bwrigley You are still instantiating an instance of IntercomGateway directly in your test:

$gw = new IntercomGateway;

If you resolve it out of the container, it should work:

$gw = resolve(IntercomGateway::class);

dump($gw->createUser(new \App\User));

bwrigley's avatar

@talinon thanks for such a fast response!

I have tried that and now I get null but I think I should get mocked no?

Talinon's avatar

Yes, you should get 'mocked' as a response. I'm not sure why you would get null

I just tested it using the following, and I get 'mocked`

        $mock = \Mockery::mock('someclass')->makePartial();
        $mock->shouldReceive('createUser')->andReturn('mocked');
        $this->app->instance('someclass', $mock);

        $gw = resolve('someclass');

        dump($gw->createUser(new \App\User));       


"mocked"
.                                                                   1 / 1 (100%)

Time: 108 ms, Memory: 20.00 MB

OK (1 test, 1 assertion)
 

bwrigley's avatar

@talinon I've tried yours and I get mocked too. So it's something to do with my class?

Talinon's avatar

Do you have final defined on your createUser() method in your IntercomGateway class?

bwrigley's avatar

my method looks like this at the moment:

    public function createUser(User $user): void
    {

        dump('Original method executed');
        try{
            Intercom::users()->create([
                'user_id' => $user->id,
                'name' => $user->fullName,
                'email' => $user->email,
                'phone' => optional($user->profile)->getPrimaryTelephone(),
                'signed_up_at' => $user->created_at,
            ]);
        } catch (\GuzzleHttp\Exception\ConnectException $e) {
            Log::Alert('Cannot connect to Intercom ' . $e->getCode() . ' ' . $e->getMessage());
        }
    }
Talinon's avatar
Talinon
Best Answer
Level 51

Try removing the return type:

From:

public function createUser(User $user): void

To:

public function createUser(User $user)

If that is the issue, you might be able to fix it by upgrading Mockery to version 1.1+

bwrigley's avatar

@talinon you genius, it works! Thank you.

However I do not understand that at all!

Talinon's avatar

@bwrigley There seems to be a problem with return types and previous versions of Mockery. Most likely because the version of Mockery you are using is based upon a PHP version that didn't support return types.

1 like
bwrigley's avatar

Thank you so much, so grateful for your time. No way I would have figured that one out!

bwrigley's avatar

@talinon so sorry to come back to this, but now I have written the full test, I seem to be having the same issue again.

Firstly, I have updated Mockery as you suggested and that is now all working fine as we had it above. Thank you.

Now the real test looks like this:

        $mock = Mockery::mock(IntercomGateway::class)->makePartial();
        $mock->shouldReceive('createUser')->andReturn('mocked');
        $this->app->instance(IntercomGateway::class, $mock);

        $user = $this->createTestUser();

        $loginCredentials = [
            'email' => $user->email,
            'password' => 'wrong_password',
        ];

        $this->fromUrl(route('login'))
            ->post('/login', $loginCredentials)
            ->assertEIMErrorExists('login_fail');

The test passes fine, but still calls the original createUser() method in the IntercomGateway class, not the mocked one.

So when I run this test I now see the dump Original method executed as before. The createTestUser() method sets up a user just for the purposes of the test, but also creates a new Intercom user which is the bit I want to mock as it's not relevant to the test.

So sorry to ask you again, but would appreciate your thoughts on what I might be doing wrong here.

Thank you!

Talinon's avatar

@bwrigley Hmm, there are only 2 things that comes to mind.

My first thought, and probably the least likely, is make sure you have imported IntercomGateway into your test file, otherwise by default, you'll be mocking & binding Tests\IntercomGatewayinto the container and hence leaving your original implementation untouched.

My second guess would be that you're not resolving it properly from within your application code. Can you show the code where you actually resolve IntercomGateway and make the call to createUser()?

bwrigley's avatar

@talinon - thank you again. I was just getting the elements of code together to show you and I'm embarrassed to admit that I found out that createUser() is actually getting called by a job which is basically running in realtime so I didn't spot it!

So sorry to waste your time! Now I just need to find out how I can fake this job in every test but not other jobs. But that's a different problem!

Thank you again. So grateful.

1 like

Please or to participate in this conversation.