質問

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 expects 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.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top