Phpunit,如何测试方法是什么"没有"?

时间:2015-02-22 18:08:21

标签: php phpunit

class Testme()
{
    public function testMe ($a)
    {
        if ($a == 1)
        {
            throw new Exception ('YAY');
        }
    }
}

所以如果它抛出异常就很容易测试

/**
 * @expectedException Exception
 */
public function test()
{
    new Testme(1);
}

但如果没有做任何事情怎么办?

public function test()
{
    new Testme(2);
 ?? ? ? ? ?
}

7 个答案:

答案 0 :(得分:9)

方案

对于函数什么都不做,您有两种可能的方案:

场景1:没有退货声明

您的功能不执行任何操作,因为您没有在其中执行操作,而包含return关键字:

public function doNothing()
{
    // Do nothing.
}

场景2:使用return语句

您的功能不执行任何操作,因为您没有在其中执行操作,并且您确实包含其中的return关键字没有表示任何返回值:

public function doNothing()
{
    // Do nothing.
    return;
}

其他场景

我将不再处理以下情况:

  1. 除了执行可以在其他对象上测试的重要操作之外,您不返回任何内容的情况。在这种情况下,您必须对修改对象的结果状态进行单元测试。

  2. 除了返回某些内容之外什么都不做的情况,那么你应该对返回值进行单元测试。

  3. 浏览PHP手册中的文档

    对于第一种情况,PHP手册记录了函数的求值表达式为null。它在这里说:http://php.net/manual/en/functions.returning-values.php在一个注释中:

      

    如果省略返回值,则返回NULL值。

    对于第二种情况,PHP手册记录了funcion的计算表达式也将是null。它在这里说:http://php.net/manual/en/function.return.php在一个注释中:

      

    如果未提供参数,则必须省略括号并返回NULL。 [...]

    结论

    因此,有明确记载了一项“什么都不做”的功能。必须评估为null

    如何测试无效的函数

    坚持你的期望:

    $this->assertNull( $sut->doNothing() );
    

    通过这种方式锻炼"你的功能,你运行它使代码覆盖完成所有行,你"期望" "什么都没发生"通过测试其评估的null值作为表达式,如文档所述。

    如何测试无效的构造函数

    然而,要测试一个构造函数......那么......常识:构造函数的目的是什么?创建某种类型的类,对吧?

    所以...我更喜欢通过检查已创建$sut来启动100%的单元测试。这是我在做新课时所做的第一次测试。这是我在课程存在之前编写的测试。最后,这就是构造函数的用途。红酒吧。然后我创建了这个类。绿色酒吧。

    假设我有一个Email类,它接受一个字符串,只有在传递有效的电子邮件时才会创建,否则会抛出异常。这与你的问题非常相似。一个只允许创建的构造函数"或者"通过爆炸系统来否认它"。

    我通常会这样做:

    //-------------------------------------------------//
    // Tests                                           //
    //-------------------------------------------------//
    
    /** @dataProvider validEmailProvider **/
    public function testCreationIsOfProperClass( string $email )
    {
        $sut = $this->getSut( $validEmail );
        $this->assertInstanceOf( Email::class, $sut );
    }
    
    /** @dataProvider invalidEmailProvider **/
    public function testCreationThrowsExceptionIfEmailIsInvalid( string $invalidEmail )
    {
        $this->expectException( EmailException::class );
        $this->getSut( $invalidEmail );
    }
    
    //-------------------------------------------------//
    // Data providers                                  //
    //-------------------------------------------------//
    
    public function validEmailProvider() : array
    {
        return
        [
            [ 'alice@example.com' ],
            [ 'bob.with-several+symbols@subdomain.another.subdomain.example.verylongTLD' ],
        ]
    }
    
    public function invalidEmailProvider() : array
    {
        return
        [
            [ 'missing_at_symbol' ],
            [ 'charlie@cannotBeOnlyTld' ],
        ]
    }
    
    //-------------------------------------------------//
    // Sut creators                                    //
    //-------------------------------------------------//
    
    private function getSut( string $email ) : Email
    {
        return new Email( $email );
    }
    

    当我使用PHP 7.0并且我将类型放在任何地方时,无论是输入参数还是返回类型,如果创建的对象不是电子邮件,getSut()函数将首先失败。

    但是,即使我写了省略返回类型,测试也会测试预期会发生什么:new Email( 'valid@example.com' );本身就是一个表达式,它会评估为"某些东西"班级Email::class

    如何测试执行某些操作的构造函数

    代码味道。构造函数可能不应该工作。如果有的话,只需存储参数。如果构造函数"确实有效"除了存储参数之外,还要考虑在getter上进行延迟处理,或者在工厂中委派该工作。

    如何测试构造函数"除了存储参数"

    之外什么都不做

    就像之前一样,然后获取数据。

    1. 在第一次测试中测试创建是某个实例。
    2. 然后在另一个不同的测试中,运用类似于getter的东西来获取在构造函数中输入的内容,即使构造函数没有任何内容(除了存储它)。
    3. 希望这会有所帮助。

答案 1 :(得分:3)

这是不可能的。添加return语句并断言结果。

class Testme()
{
    public function testMe ($a)
    {
        if ($a == 1)
        {
            throw new Exception ('YAY');
        }

        return true;
    }
}

然后

$object = new Testme();
$this->assertTrue($object->testMe(2));

答案 2 :(得分:3)

在PHPUnit 7.2+中,您也可以使用TestCase::expectNotToPerformAssertions()

public function test()
{
    // ...

    $this->expectNotToPerformAssertions();
}

这与@doesNotPerformAssertions批注具有相同的行为。

答案 3 :(得分:1)

注意:此解决方案的积分转到this related answer。上下文可能看起来有点不同,但解决方案/解决方法的工作方式相同。测试不抛出异常与测试没有返回值的方法是一样的。

根据this issue thread,没有内置的解决方案来测试PHPUnit中的DoesNotThrowException之类的东西。

所以是的,一个解决方案是从你的方法返回一些虚拟值,比如

public function testMe ($a)
{
    if ($a == 1) { throw new Exception ('YAY'); }

    return true;
}

然后在测试中断言它。但是,如果您不想仅为测试更改代码,则可以解决此问题:

public function testExceptionIsNotThrown()
{
    try {
        new Testme(2);
    }
    catch(Exception $e) {
        /* An exception was thrown unexpectedly, so fail the test */
        $this->fail();
    }

    /* No exception was thrown, so just make a dummy assertion to pass the test */
    $this->assertTrue(true);
}

它可能看起来很黑,而且不是很直观,但是if it's stupid but it works, it's not stupid

答案 4 :(得分:1)

我遇到了同样的问题。确保"没有"已经发生了它,只需要在你的单元测试中称呼你的方法。如果失败,测试无论如何都会失败。

如果您只是在没有@expectedException注释的情况下调用您的方法

public function test()
{
    new Testme(1);
}

您将收到错误

There was 1 error:

1) Testme::testMe
Exception: YAY

答案 5 :(得分:1)

2018 +

如今,最佳实践是完全针对以下情况进行注释:

/**
 * @doesNotPerformAssertions
 */
public function testSomething()
{
    $someService = new SomeObject();
    $someService->shallNotFail();
}

答案 6 :(得分:0)

这是一个非常有趣的问题,尽管编写了很多答案,但是似乎没有一个人能够正确回答这个问题,因为您使用类要求过,让我以此方式进行解释。

请记住,您在类中创建的实例方法应该只有两个意图。

  1. 它可以更改类的状态(更改类属性,例如私有变量)
  2. 它返回类的状态(getters)
除非是静态方法,否则除此以外的任何事情都是没有意义的。例如 如果你有这样的课程

class Foo {

   private $prop = null;
   public function fooMethod() {
      $this->prop = "string";
   }
   public function getProp() {
     return $this->prop;
   }
}

方法fooMethod()不会返回任何东西,但是会影响类中$ prop属性的状态,您可以通过

测试该方法
$this->assertNotNull( $instance->getProp() );

因为您知道是否运行了此方法,所以应该影响prop $ prop并更改该变量的状态。

其他情况:我的方法不会更改状态,也不会返回任何状态变量。

然后该方法为static。它不应该是实例方法,静态方法通常具有返回类型,因为它们既不能影响类的状态,也不能返回状态变量。这限制了静态方法将结果存储在某个地方(除非将它们存储在全局变量中,否则不要这样做),因此,它肯定会返回一些输出。如果不想返回输出,则可以考虑从静态方法返回布尔值。