PHPUnit mocking an object don't work
-
04-07-2021 - |
質問
I'm new to mocking objects in PHPUnit and can't get it working. I am building an extension of the current SensioGeneratorBundle
(for Symfony2). I use PHPUnit 3.7 installed via PEAR
. It is running on PHP 5.3.5 (as PEAR is installed in that version).
My stripped classes are:
ControllerGenerator.php
class ControllerGenerator extends Generator
{
// ...
public function generate(BundleInterface $bundle, $controller, array $actions = array())
{
// ...
}
}
GenerateControllerCommand.php
class GenerateControllerCommand extends ContainerAwareCommand
{
private $generator;
/**
* @see Command
*/
public function configure()
{
// ...
}
public function execute(InputInterface $input, OutputInterface $output)
{
// ...
$generator = $this->generator;
$generator->generate($bundle, $controller);
// ...
}
protected function getGenerator()
{
if (null === $this->generator) {
$this->generator = new ControllerGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/bundle');
}
return $this->generator;
}
public function setGenerator(ControllerGenerator $generator)
{
$this->generator = $generator;
}
}
GenerateControllerCommandTest.php
class GenerateControllerCommandTest extends GenerateCommandTest
{
public function testNonInteractiveCommand()
{
$bundle = 'FooBarBundle';
$controller = 'PostController';
$input = array(
'command' => 'generate:controller',
'--bundle' => $bundle,
'--controller' => $controller,
);
$application = $this->getApplication();
$commandTester = $this->getCommandTester($input);
$generator = $this->getGenerator();
$generator
->expects($this->once())
->method('generate')
->with($this->getContainer()->get('kernel')->getBundle($bundle), $controller)
;
$commandTester->execute($input, array('interactive' => false));
}
protected function getCommandTester($input = '')
{
return new CommandTester($this->getCommand($input));
}
protected function getCommand($input = '')
{
return $this->getApplication($input)->find('generate:controller');
}
protected function getApplication($input = '')
{
$application = new Application();
$command = new GenerateControllerCommand();
$command->setContainer($this->getContainer());
$command->setHelperSet($this->getHelperSet($input));
$command->setGenerator($this->getGenerator());
$application->add($command);
return $application;
}
protected function getGenerator()
{
// get a noop generator
return $this
->getMockBuilder('Sensio\Bundle\GeneratorBundle\Generator\ControllerGenerator')
->disableOriginalConstructor()
->setMethods(array('generate'))
->getMock()
;
}
}
When I run PHPUnit, I keep getting this error:
$ phpunit Tests\Command\GenerateControllerCommandTest
PHPUnit 3.7.0 by Sebastian Bergmann.
Configuration read from E:\Wouter\web\wamp\www\wjsnip\vendor\sensio\generator-bundle\Sensio\Bundle\GeneratorBundle\phpunit.xml.dist
F
Time: 2 seconds, Memory: 7.25Mb
There was 1 failure:
1) Sensio\Bundle\GeneratorBundle\Tests\Command\GenerateControllerCommandTest::testNonInteractiveCommand
Expectation failed for method name is equal to <string:generate> when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.
E:\Wouter\web\wamp\bin\php\php5.3.5\PEAR\phpunit:46
FAILURES!
Tests: 1, Assertions: 7, Failures: 1.
Why am I getting this error? I think I called the generate
command in the GenerateControllerCommand::execute
method? Am I doing something wrong, possible true? Or is this a bug in PHPunit?
解決
In short
You generate two differnt $generator
objects. The call happens to one while the other expect
s it.
Longer
You change the behavior protected function getGenerator()
but the original function expects that calling that function populates $this->generator
.
Your test is not working as you and the function expects to always get the same generator and with your overwrite the function returns two different objects.
You set the expected call on one and the call happens to the object.
Just looking at:
$generator = $this->getGenerator();
$generator
->expects($this->once())
->method('generate')
->with($this->getContainer()->get('kernel')->getBundle($bundle), $controller)
;
$commandTester->execute($input, array('interactive' => false));
}
The $generator
variable is not put in any object scope anywhere and hence no calls can happen on it as every call to $this->getGenerator()
produces a new object that is not stored anywhere.
So in
protected function getApplication() {
//...
$command->setGenerator($this->getGenerator());
//...
}
you have a different object than you have in your test case.