测试在PHPUnit中不会调用任何方法(无论名称)?

时间:2012-12-19 11:26:22

标签: php unit-testing phpunit

部分考试科目:

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

        if(!$container->hasParameter('gremo_highcharts.options_provider')) {
            return;
        }

        // ...
    }
}

我想断言:

  • hasDefinition()使用参数'gremo_highcharts'调用将返回false
  • 方法process()返回,即不会调用其他方法

一种解决方案是断言后续调用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);
}

但它似乎不是一个优雅的解决方案。

4 个答案:

答案 0 :(得分:3)

要表达any method,您可以使用$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() {}
}

输出:

PHPUnit 3.7.10-4-ga0bccf3 by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 6.50Mb

OK (1 test, 1 assertion)

在方法调用中注释时

$mock->bar();

然后输出:

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.

仅允许一个方法调用但不调用其他方法

这看起来有点难看但也有效

<?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() {}
}

作品。在其他方法中进行注释时,它会像上面那样失败。

如果想要允许调用多个方法,它会变得更加冗长:

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

答案 1 :(得分:2)

这是一个例外情况吗?如果是这样,您可以更改第一次返回(为什么还要返回无效?)到抛出特定的异常。然后使用PHPUnit验证是否实际捕获了该特定异常。

编辑: 还有Phake你可以在测试结束时写这样的东西:(类似于用PHPUnit模拟对象调用 - &gt; never())

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

这会在存根方法调用和验证已调用方法(存根或不存在)之间产生区别。

答案 2 :(得分:1)

在测试此类方法时,请尝试全局了解。不要下降到ifreturn s的级别,将其提高。通过声明在return之后没有进行其他调用,您真正测试的是PHP的本机语句,而不是您的方法的逻辑。这就像你不相信return。接受我的话,在return语句后,在该方法中没有执行任何操作:)

相反,测试你的方法的逻辑!

逻辑是什么?

嗯,根据您的代码,您有此课程AddOptionsProviderArgumentPass及其process方法。 pocess方法采用ContainerBuilder并以某种方式处理它。因此,您需要测试的是process方法可以很好地完成工作。方法中的if代表了一些需要满足的约束才能成功处理ContainerBuilder

您如何理解process是否成功?

按其返回类型。

如果它没有返回任何内容怎么办?

检查其副作用。您对ContainerBuilder所做的事情是什么。

所以,我就是这样看的。

/**
 * @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;
}

最后一点

不要依赖if的顺序,它可以改变!

答案 3 :(得分:0)

也许创建Phake模拟并调用

Phake::verifyNoInteraction($mock);

是解决此问题的好方法。这是手册的链接:https://phake.readthedocs.io/en/2.1/method-verification.html#verifying-no-interaction-with-a-mock-so-far