How to mock an aliased class with a method that returns an instance of itself?
Asked Answered
A

2

6

I've been successfully using Mockery with PHPUnit tests lately. Yet, there is a dependency in a project I'm currently working that uses static method calls to interact with an API. I'm struggling to test one particular use case and it feels like I'll find other like this during the development roadmap.

Using this class as an example:

namespace Name\Space;
class User
{
    /**
     * @return \Name\Space\User[]
     */
    public static function list(): array
    {
        // ...
    }
    public static function create(array $attrs): User
    {
        // ...
    }
}

In case I just want to assert a method returns a primitive type, such as an array:

Mockery::mock('alias:\Name\Space\User')
    ->shouldReceive('list')
    ->andReturn([]);

It works fine, primarily because I'm not testing the array contents.

However, I have to call the create method, which returns an instance of the class itself (User). If I do something like this:

$user = new \Name\Space\User();
Mockery::mock('alias:\Name\Space\User')
    ->shouldReceive('create')
    ->andReturn($user);

The alias, obviously, won't work because the class was already loaded through the autoloader (composer's, in this case).

Does anyone have a suggestion on how to workaround this?

Anele answered 29/3, 2019 at 18:48 Comment(3)
I'm not an expert in Mockery, but, what about creating User in a closure? $user = Mockery::mock('overload:\Name\Space\User') ->shouldReceive('create') ->andReturnUsing(function() { return new \Name\Space\User(); });Trocar
@Trocar Thanks! This approach works. Could you post it as an answer? I can then award you the bounty.Anele
I posted the example code.Trocar
T
3

What about creating User in a closure?

<?php

$user = Mockery::mock('overload:\Name\Space\User')
    ->shouldReceive('create')
    ->andReturnUsing(function() {
              return new \Name\Space\User();
    });
Trocar answered 8/4, 2019 at 21:58 Comment(1)
What if we want to mock calls to the returned new \Name\Space\User() as well? and how do we make Mockery to expect calls to a specific instance of User, in case create is called multiple times?Gamaliel
W
0

Mocking static stuff is always painful. I would recommend creating a Proxy object that is calling the static API calls and just returns the API results and inject this object everywhere you need to call the API.

This way it is easy to test by simply mocking the proxy object.

The proxy object itself can then be tested in an end to end test outside of the pure unit test scope.

You can still do more invasive stuff like this https://www.pagemachine.de/blog/mocking-static-method-calls/?cn-reloaded=1

But writing code that doesn't belong to your unit tests purely to make something testable doesn't feel right to me.

Wallet answered 4/4, 2019 at 8:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.