Reputation: 48899
Part of the 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 assert that:
hasDefinition()
call with parameter 'gremo_highcharts' will return false
process()
returns, that is no other method will be invokedOne solution would be asserting about the subsequent call to hasParameter()
:
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 it doesn't seem an elegant solution.
Upvotes: 7
Views: 1100
Reputation: 3157
Maybe creating Phake mocks and calling
Phake::verifyNoInteraction($mock);
is a good solution to this problem. Here's a link to the manual: https://phake.readthedocs.io/en/2.1/method-verification.html#verifying-no-interaction-with-a-mock-so-far .
Upvotes: 0
Reputation: 2582
When testing such methods, try to see the big picture. Don't descend to the level of if
s and return
s, take it higher. By asserting that no other call was made after return
, what You really test are the PHP's native statements, not the logic of Your method. It's like You don't trust the return
s. Take my word for it, after return
statement nothing is executed in that method :)
Instead, test the logic of Your method!
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, what You need to test is that the process
method does its job well. Your if
s in the method represent some constraints which need to be satisfied in order to successfully process the ContainerBuilder
.
How do You understand if the process
was successful?
By its return type.
What if it doesn't return anything?
Check for its side effects. Which are the things You do to the ContainerBuilder
.
So, here 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
Don't rely on the order of if
s, it can change!
Upvotes: 1
Reputation: 38961
To express any method
you can use $this->anything()
.
<?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)
$mock->bar();
it then 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.
This 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 the other method calls it fails like above.
If one wants to allow for multiple methods to be called it gets a little more verbose:
$this->logicalNot(
$this->logicalOr(
$this->matches('foo'),
$this->matches('baz'),
$this->matches('buz')
)
)
Upvotes: 3
Reputation:
Is it an exceptional case? If so, you could change the first return (why are you returning void anyway?) to throwing a specific exception. Then use PHPUnit to verify that that specific exception is actually caught.
Edit: Also with Phake you could 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 creates a distinction between stubbing method calls and verifying that methods (stubbed or not) have been called.
Upvotes: 2