Pagemachine.de/ Blog/ Mocking static method calls in PHP

Mocking static method calls in PHP

Static method calls are sometimes necessary due to an existing API or because there is no alternative like injectable services. This makes them basically impossible to mock for unit testing.

An example for TYPO3:

class MyClass {
  public function doSomething() {
    $text = LocalizationUtility::translate('foo');
    // Do something with $text
  }
}

The call to LocalizationUtility::translate() is static and thus there's no way to mock it to encapsulate a unit test.

class MyClassTest extends \PHPUnit_Framework_TestCase {
  /**
   * @test
   */
  public function doesWhatIsExpected() {
    $myClassInstance = new MyClass();

    // How to ensure that the LocalizationUtility::translate() call returns a predefined text here?
    $myClassInstance->doSomething();
  }
}

This usually only leaves us one of two choices: try to mock whatever the static method call does (bad since that’s not part of our subject) or add @codeCoverageIgnore and call it a day (obviously bad, too.)

However, mocking static method calls is indeed possible by using a trait and doing the method calls slightly different. Let’s start with the trait:

/**
 * Trait for static method calls
 *
 * This is useful to make static method calls mockable in tests.
 *
 * This trait must not be used more than once in a class hierarchy,
 * otherwise endless call loops occur for parent method calls.
 * See bugs.php.net/bug.php for details.
 */
trait StaticCalling {
  /**
   * Performs a static method call
   *
   * Note that parent::class should be used instead of 'parent'
   * to refer to the actual parent class.
   *
   * @param string $classAndMethod Name of the class
   * @param string $methodName Name of the method
   * @param mixed $parameter,... Parameters to the method
   * @return mixed
   */
  protected function callStatic($className, $methodName) {
    $parameters = func_get_args();
    $parameters = array_slice($parameters, 2); // Remove $className and $methodName

    return call_user_func_array($className . '::' . $methodName, $parameters);
  }
}

The reason for using a trait here is access to protected methods which is impossible with e.g. a utility method.

Now the class above can be changed like this:

class MyClass {
  use StaticCalling;
  public function foo() {
    $text = $this->callStatic(LocalizationUtility::class, 'translate', 'foo');
    // Do something with $text
  }
}

Functionality-wise there was no change, in the end the same method call is performed. However, we are now actually able to rewrite the test and mock the method call:

class MyClassTest extends \PHPUnit_Framework_TestCase {
  /**
   * @test
   */
  public function doesWhatIsExpected() {
    $myClassInstance = $this->getMockBuilder(MyClass::class)
      ->setMethods(['callStatic'])
      ->getMock();

    $myClassInstance
      ->method('callStatic')
      ->with(LocalizationUtility::class, 'translate', 'foo')
      ->willReturn('mocked');
    
    $myClassInstance->doSomething();
  }
}

The key is that we now have a central method callStatic() we can mock in tests. Every static method call should use this method, calls to parent classes should be done with parent::class instead of 'parent' to ensure the correct 'parent' is used.

Happy testing!

Feedback? With pleasure!
You may like to comment our tutorial and/or if you consider our advice helpful,
feel free to share it. Thanks a lot!

Wir machen Ihre Website wow!

Wir sind Profis, wenn es um Ihre Website geht. Design, Programmierung, SEO, Support.

Experten fürs TYPO3-Update

Wollen Sie mehr darüber wissen, wie wir vorgehen?

Unsere Kundinnen und Kunden

Unser Newsletter

Pro Quartal versenden wir einen Newsletter, der spannende Neuigkeiten zu den Themen TYPO3, Webdesign, SEO und Trends enthält.

Don’t talk about!

Über 100 Mitarbeiter:innen!

Der FGTCLB: Fünf Agenturen, ein Netzwerk, seit 2017. Wir sind unabhängig, profitieren aber von einem geteilten Pool an Ressourcen und Erfahrung, auch aus gemeinsam realisierten Projekte.

Ein Team

Was haben Sie davon? Ganz einfach, Ihre Projekte werden schneller fertig. Wir gleichen Arbeitsspitzen aus. Und Sie profitieren von mehr Know-how, gerade bei kniffligen Aufgaben.

Pagemachine AG

Solmsstraße 6a
60486 Frankfurt am Main

Tel.: +49 69 260 99 70 30
E-mail: info@pagemachine.de

Kontakt aufnehmen

Haben Sie Fragen oder möchten Sie ein kostenloses, unverbindliches Angebot? Wir kommen gerne bei Ihnen vorbei oder laden Sie auf einen Kaffee bei uns ein.


© PAGEMACHINE AG 2024