5

I am desperately trying to unit test a module for a shopsystem. That shop system uses static methods which I have to call in my functions I want to test.

public function toTest() {
    $value = \Context::getData();
    return $value;
}

Now how can I unit test that function while mocking this static call? I tried using AspectMock but that does not work because it apparently needs access to the original \Context class which is not available since it's an external system. I also tried using class_alias to create my own Context class but that does not work either because I need different Context output depending on which function I am testing. And setting class_alias multiple times for different tests does not work because the same class can't be declared multiple times and @runTestsInSeparateProcesses did not have the expected effect.

Edit: None of the duplicates provided a viable solution to my situation, so I don't think this is a duplicate. With no access to the shopsystem code and especially with hard to maintain code like that, PHP does not make it easy to unit test this. Also the solution I found should help others with similar settings.

alobeejay
  • 247
  • 3
  • 12
  • Believe this is a duplicate of [Mocking static methods in PHP](https://stackoverflow.com/questions/2357001/phpunit-mock-objects-and-static-methods) which already exists in stackoverflow – Diogo Santo Nov 26 '18 at 13:09
  • Possible duplicate of [phpunit static called method in method](https://stackoverflow.com/questions/42854137/phpunit-static-called-method-in-method) – Dirk Scholten Nov 26 '18 at 13:09
  • I don't know if this is a realistic example of your code, but if it is, there is not much to test here. Instead you should test the `getData()` method. – jeroen Nov 26 '18 at 13:11
  • @DiogoSanto This duplicate is for mocking a method that would be in my own code not in an external system like here. At least as far I understand. – alobeejay Nov 26 '18 at 13:18
  • @jeroen The getData method is not my own so I can't test it. Also this is a very simplified example. I do stuff with $value in between. – alobeejay Nov 26 '18 at 13:19
  • In that case I would move the call to `\Context::getData()` to a separate, private method (almost like you have now...) so that you can easily mock that new method to test your `toTest()` method. – jeroen Nov 26 '18 at 13:21
  • @jeroen Thanks for your help. Could you give me a small code example how I would mock that private method? I'm having real trouble with this. – alobeejay Nov 26 '18 at 13:27

1 Answers1

4

I could solve my issue with the Mockery library. I tried out a few but nothing worked. With Mockery everything seems possible now. This link really helped: https://robertbasic.com/blog/mocking-hard-dependencies-with-mockery/

You can easily mock static calls to classes that don't belong to you:

public function methodToTest() {
    return \Context::getData();
}

public function testMethodToTest() {
    m::mock('alias:\Context')
        ->shouldReceive('getData')
        ->andReturn('foo');
}

And even instantiations for classes you don't have access to:

public function methodToTest() {
    $obj = new \Category(5);
    return $obj->id;
}

public function testMethodToTest() {
    m::mock('overload:\Category')
        ->shouldReceive('__construct')
        ->with(5)
        ->andSet('id', 5);
}

But you have to keep in mind that you need the two phpunit annotations at the beginning of the class:

* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
alobeejay
  • 247
  • 3
  • 12