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?

Gustavo Straube
  • 3,744
  • 6
  • 39
  • 62
  • 2
    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(); });` – SilvioQ Apr 03 '19 at 21:19
  • @SilvioQ Thanks! This approach works. Could you post it as an answer? I can then award you the bounty. – Gustavo Straube Apr 08 '19 at 18:06
  • I posted the example code. – SilvioQ Apr 08 '19 at 21:59

2 Answers2

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();
    });
SilvioQ
  • 1,972
  • 14
  • 26
  • 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? – Top-Master Apr 10 '19 at 07:29
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.

sfeldmann
  • 289
  • 1
  • 8