Testing that no methods (regardless of name) will be called in PHPUnit?

Test Subject:

class AddOptionsProviderArgumentPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { if(!$container->hasDefinition('gremo_highcharts')) { return; } if(!$container->hasParameter('gremo_highcharts.options_provider')) { return; } // ... } } 

I want to argue that:

  • hasDefinition() call with the parameter 'gremo_highcharts' will return false
  • The process() method is returned, i.e. no other method will be called

One solution will claim a subsequent hasParameter() call:

 public function testProcessWillReturnIfThereIsNoServiceDefinition() { $container = $this->getMockedContainerBuilder(); $pass = new AddOptionsProviderArgumentPass(); $container->expects($this->once()) ->method('hasDefinition') ->with($this->equalTo('gremo_highcharts')) ->will($this->returnValue(false)); // Expects that hasParameter() is never invoked $container->expects($this->never()) ->method('hasParameter'); $pass->process($container); } 

But this does not seem to be an elegant solution.

+6
source share
3 answers

To express any method , you can use $this->anything() .

Full example:

 <?php class fooTest extends PHPUnit_Framework_TestCase { public function testNeverCallNothing() { $mock = $this->getMock('mockMe'); $mock->expects($this->never())->method($this->anything()); //$mock->bar(); } } class mockMe { public function bar() {} } 

Outputs:

 PHPUnit 3.7.10-4-ga0bccf3 by Sebastian Bergmann. . Time: 0 seconds, Memory: 6.50Mb OK (1 test, 1 assertion) 

When commenting on a method call

 $mock->bar(); 

he outputs:

 PHPUnit 3.7.10-4-ga0bccf3 by Sebastian Bergmann. F Time: 0 seconds, Memory: 6.50Mb There was 1 failure: 1) fooTest::testNeverCallNothing mockMe::bar() was not expected to be called. .../tests/neverCallMe/fooTest.php:9 FAILURES! Tests: 1, Assertions: 0, Failures: 1. 

Allowing only one method call, but not calling other methods

It looks a little ugly, but also works

 <?php class fooTest extends PHPUnit_Framework_TestCase { public function testNeverCallNothing() { $mock = $this->getMock('mockMe'); $mock->expects($this->once())->method('foo'); $mock->expects($this->never())->method( $this->logicalNot($this->matches('foo')) ); $mock->foo(); //$mock->bar(); } } class mockMe { public function bar() {} public function foo() {} } 

Works. When commenting in another method causes it to fail, as shown above.

If you want to allow the use of several methods, it becomes a little more verbose:

 $this->logicalNot( $this->logicalOr( $this->matches('foo'), $this->matches('baz'), $this->matches('buz') ) ) 
+3
source

Is this an exceptional case? If so, you can change the first return (why are you returning void anyway?) To by throwing a specific exception . Then use PHPUnit to make sure that this particular exception is actually caught.

Edit: Also with Phake, you can write something like this at the end of your test: (similar to calling → never () with PHPUnit Mock Objects)

 Phake::verify($container, Phake::times(0))->hasParameter(); 

This makes a distinction between calls to the stubbing method and checking that the methods were called (truncated or not).

+2
source

When testing such methods, try to see a large picture. Do not stoop to the level of if and return s, raise it higher. Claiming that there was no other call after return , what you are really testing are the native PHP statements, not the logic of your method. It looks like you do not trust return s. Take my word for it, after return nothing is done in this method :)

Check the logic of your method instead!

What is the logic?

Well, according to your code, you have this class AddOptionsProviderArgumentPass and its process method. The pocess method takes a ContainerBuilder and processes it somehow. So, you need to verify that the process method does its job well. Your if in the method represents some of the restrictions that must be met to successfully handle ContainerBuilder .

How do you understand if the process successful?

By its return type.

What if it returns nothing?

Check out its side effects. What are you doing with ContainerBuilder .

So, this is how I see it.

 /** * @test */ public function shouldNotProcessWithoutHighcharts() { // Arrange $container = $this->buildContainer(); $container->removeDefinition('gremo_highcharts'); $pass = new AddOptionsProviderArgumentPass(); // Act $pass->process($container); // Assert $this->assertFalse($container->hasWhatYouNeedItToHaveAfterProcessing()) } /** * @test */ public function shouldNotProcessWithoutHighchartsOptionsProvider() { // Arrange $container = $this->buildContainer(); $container->getParameterBag()->remove('gremo_highcharts.options_provider'); $pass = new AddOptionsProviderArgumentPass(); // Act $pass->process($container); // Assert $this->assertFalse($container->hasWhatYouNeedItToHaveAfterProcessing()) } private function buildContainer() { $container = new ContainerBuilder(); $container->setParameter('gremo_highcharts.options_provider'); $container->setDefinition('gremo_highcharts'); return $container; } 

Last note

Do not rely on if s order, it can change!

+1
source

All Articles