Mock static method php

Jaan Paljasma | Tech Blog

Experienced, hands-on technology executive with 20 years of expertise in creating cutting-edge technology solutions. Follow @jpaljasma on Twitter.

Stubbing and Mocking Static Methods with PHPUnit

  • Get link
  • Facebook
  • Twitter
  • Pinterest
  • Email
  • Other Apps

PHPUnit has ability to stub and mock static methods.

 public static function helper() < return 'foo'; >> ?>
getMockClass( 'Foo', /* name of class to mock */ array('helper') /* list of methods to mock */ ); $class::staticExpects($this->any()) ->method('helper') ->will($this->returnValue('bar')); $this->assertEquals( 'bar', $class::doSomething() ); > > ?>

The new staticExpects() method works similar to the non-static expects() variant. This approach only works for the stubbing and mocking of static method calls where caller and callee are in the same class. This is because static methods are death to testability:

«Unit-Testing needs seams, seams is where we prevent the execution of normal code path and is how we achieve isolation of the class under test. Seams work through polymorphism, we override/implement class/interface and then wire the class under test differently in order to take control of the execution flow. With static methods there is nothing to override. Yes, static methods are easy to call, but if the static method calls another static method there is no way to override the called method dependency.»

Few best practices

One Assertion per Test

Few tips and best practices on how to write your unit tests efficiently. One of the best advises would be «one test = one assertion». You may think that cutting corners may be a good idea; well, think again. Here’s a little test scenario — you need to test if the function returns string. You name this function as «testIfFunctionReturnsString». And while asserting string, you may be tempted to also do other assertions — why not test for that as well?

assertGreaterThan(0,strlen($string)); $this->assertContains("99",$string); > ?>

In this case you are testing string length and also if string contains «99». Second assertion will fail, yet the result can be deceptive and might cause confusion what caused the error.

Читайте также:  Python convert bin to hex

Be descriptive about what you are testing

Negate(1); $this->assertEquals(-1, $b->getAmount()); > > ?>
vendor/bin/phpunit --stderr --testdox tests
PHPUnit 3.7.24 by Sebastian Bergmann. Money [x] Can be negated

Conclusion

Start testing your PHP applications today if you haven’t already.

Источник

PHPUnit: Testing static calls

When you’re writing code, sometimes it’s necessary to use static calls. Maybe the only way to interface with an external library is through static calls. I must admit, it’s just easy to work with a static call. Until you have to assert in a unit test that you’re actually calling the static method. Then it gets more difficult.

Stubbing and mocking internal static methods

As Sebastian Bergmann explains in one of his blogposts, since PHPUnit 3.5 it’s possible to stub & mock static methods. In the same class. Here’s how it goes.

Sample class with internal static method call

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 
php class Foo   public static function bar()    // some logic here, and then.  static::baz($somevar);  return 'bar';  >  public static function baz($somevar)    // some more logic  > > 

The unit test goes like this:

Unit test internal static method call

1 2 3 4 5 6 7 8 9 10 11 12 13 14 
php class FooTest extends \PHPUnit_Framework_TestCase   public function testBar()    $class = $this->getMockClass('Foo', array('baz'));  $class::staticExpects($this->any())  ->method('baz')  ->with($this->equalTo('somevar value'));  $this->assertEquals('bar', $class::bar());  > > 

So what happens here? The key is that we have 2 static methods, and in the first static method we do a static call to the second static method. All in the same class.

We tell the getMockClass method to give us a mock classname for our Foo class, and we want to stub method baz . Once we have this classname, we can use staticExpects for our expectations.

Then we do our regular static call to the bar method. But instead of doing it on our Foo class, we do it on the mock classname. As such, the mock can use the stub we created when internally static::baz is called.

Stubbing and mocking external static methods

So far that wasn’t too difficult. But how many times have you written code like this? Personally, I don’t ever write code like that. However, sometimes I have to work with an external library and then static calls might be the only available interface.

Sample class with external static method call

php class Foo   public function bar()    // some logic here, and then.  Log::message('We have just executed Foo::bar()');  > > 

As you can see, the interface to the Log class makes us use a static call to log a message. Assume that you want to know if you actually do the Log::message call in a unit test, then we’re screwed since the static call is external.

There is a way around that. It’s a hassle and it’s not beautiful. First we have to refactor the external static call a bit:

Sample class with external static method call

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 
php class Foo   protected $loggingClass = 'Log';  public function bar()    // some logic here, and then.  forward_static_call_array(  array($this->loggingClass, 'message'),  array('We have just executed Foo::bar()');  );  > > 

Instead of calling Log::mesage directly, we use forward_static_call_array. The name of our log class is now defined in a property, and we use that property in the forward_static_call_array method. Can you see where this is going? We’re kind of injecting the Log classname dependency. Not pretty, but it works!

Unit test external static method call

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 
php class FooTest extends \PHPUnit_Framework_TestCase   public function testBar()    // create a mock class with expectations  $class = $this->getMockClass('Log', array('message'));  $class::staticExpects($this->once())  ->method('message');  $foo = new Foo();  // set the mock classname in the property  $reflProp = new \ReflectionProperty('Foo', 'loggingClass');  $reflProp->setAccessible(true);  $reflProp->setValue($foo, $class);  // call bar()  $foo->bar();  > > 

Same as before, we use getMockClass to get a mock classname for the Log class. Then we set this classname in our $loggingClass property. Since it’s a protected property, I can only do that through Reflection. I just made the property protected to make it more difficult ;–).

So here we go, we were able to test an external static method call. It’s up to you to decide if you want to refactor your code like this. And it’s also up to you to decide if you want to actually assert these kinds of calls. I agree, we don’t really want to verify a call to a logger. But that was just an example.

Источник

Mocking Static Methods¶

Phake can be used to verify as well as stub polymorphic calls to static methods. It is important to note that you cannot verify or stub all static calls. In order for Phake to record or stub a method call, it needs to intercept the call so that it can record it. Consider the following class

class StaticCaller   public function callStaticMethod()   Foo::staticMethod(); > > 

You will not be able to stub or verify the call to Foo::staticMethod() because the call was made directly on the class. This prevents Phake from seeing that the call was made. However, say you have an abstract class that has an abstract static method.

abstract class StaticFactory   protected static function factory()   // . > public static function getInstance()   return static::factory(); > > 

In this case, because the static:: keyword will cause the called class to be determined at runtime, you will be able to verify and stub calls to StaticFactory::factory(). It is important to note that if self::factory() was called then stubs and verifications would not work, because again the class is determined at compile time with the self:: keyword. The key thing to remember with testing statics using Phake is that you can only test statics that leverage Late Static Binding: http://www.php.net/manual/en/language.oop5.late-static-bindings.php

The key to testing static methods using Phake is that you need to create a “seam” for your static methods. If you are not familiar with that term, a seam is a location where Phake is able to override and intercept calls to your code. The typical seem for Phake is a parameter that allows you to pass your object. Typically you would pass a real object, however during testing you pass in a mock object created by Phake. This is taking advantage of an instance seam.

Thankfully in php now you can do something along the lines of $myVar::myStatic() where if $myVar is a string it resolves as you would think for a static method. The useful piece though is that if $myVar is an object, it will resolve that object down to the class name and use that for the static.

So, the general idea here is that you can take code that is in class Foo:

class Foo   public function doSomething()   // . code that does stuff . Logger::logData(); > > 

which does not provide a seam for mocking Logger::logData() and provide that seem by changing it to:

class Foo   public $logger = 'Logger'; public function doSomething()   // . code that does stuff . $logger = $this->logger; $logger::logData($data); > > 

Now you can mock logData as follows:

class FooTest   public function testDoSomething()   $foo = new Foo(); $foo->logger = Phake::mock(Logger::class); $foo->doSomething(); Phake::verifyStatic($foo->logger)->logData(Phake::anyParameters()); > > 

Phake has alternative methods to handle interacting with static methods on your mock class. Phake::mock() is still used to create the mock class, but the remaining interactions with static methods use more specialized methods. The table below shows the Phake methods that have a separate counterpart for interacting with static calls.

Instance Method Static Method
Phake::when() Phake::whenStatic()
Phake::verify() Phake::verifyStatic()
Phake::verifyCallMethodWith() Phake::verifyStaticCallMethodWith()
Phake::whenCallMethodWith() Phake::whenStaticCallMethodWith()
Phake::reset() Phake::resetStatic()

If you are using Phake to stub or verify static methods then you should call Phake::resetStaticInfo() in the the tearDown() method. This is necessary to reset the stubs and call recorder for the static calls in the event that the mock class gets re-used.

© Copyright 2010-2021, Mike Lively Revision 9fe07077 .

Versions latest 4.0 3.1 3.0 2.3 2.2 2.1 2.0 1.0 Downloads pdf html epub On Read the Docs Project Home Builds Free document hosting provided by Read the Docs.

Источник

Оцените статью