如何使用PHPUnit测试没有返回值的方法?

23

我正在尝试测试我编写的以下类中的方法(比所示更多的函数,基本上每个is_*()方法都有一个函数):

class Validate {
  private static $initialized = false;

  /**
  * Construct won't be called inside this class and is uncallable from the outside. This prevents
  * instantiating this class. This is by purpose, because we want a static class.
  */
  private function __construct() {}

  /**
  * If needed, allows the class to initialize itself
  */
  private static function initialize()
  {
    if(self::$initialized) {
      return;
    } else {
      self::$initialized = true;
      //Set any other class static variables here
    }
  }

  ...

  public static function isString($string) {
    self::initialize();
    if(!is_string($string)) throw new InvalidArgumentException('Expected a string but found ' . gettype($string));
  }

  ...

}

当我测试方法对无效输入是否抛出异常时,它非常有效!但是,当我测试方法是否按预期工作时,PHPUnit会抱怨测试中没有断言。具体错误如下:

# RISKY This test did not perform any assertions

然而,我没有任何值可以断言,所以我不确定如何克服这个问题。

我已经阅读了一些关于测试静态方法的文章,但那似乎主要涵盖了静态方法之间的依赖关系。此外,即使非静态方法也可能没有返回值,那么如何解决这个问题呢?

供参考,我的测试代码:

class ValidateTest extends PHPUnit_Framework_TestCase {
  /**
  * @covers ../data/objects/Validate::isString
  * @expectedException InvalidArgumentException
  */
  public function testIsStringThrowsExceptionArgumentInvalid() {
    Validate::isString(NULL);
  }

  /**
  * @covers ../data/objects/Validate::isString
  */
  public function testIsStringNoExceptionArgumentValid() {
    Validate::isString("I am a string.");
  }
}

2
为什么该方法不会返回true/false?当传递非字符串值作为参数时,为什么会出现异常,因为一个测试是否为字符串的方法显然应该期望非字符串值或者该方法没有实用性。这似乎是一个构思不周的方法。 - Mike Brant
@MikeBrant 它不返回任何值,因为它没有这个需要。我在我的代码中使用该方法作为断点/断言 - 如果该方法不抛出异常,则我的代码将继续正常运行。如果它抛出异常,则必须处理该异常。根本没有理由从这些方法中返回任何东西,甚至更少的理由编写if/else语句并花费时间检查这些返回值。我希望使用异常而不是TRUE/FALSE,因为异常可以停止代码执行。如果您在Java(或大多数类型化语言)中将int放在应该放置数组的位置,则会收到异常。我希望获得类似的功能。 - Matthew Herbst
4
当你有这样的验证方法时,为什么验证类要决定调用者的行为呢?也许调用此类的代码正在尝试基于值是字符串来做出某些决策,而完全期望其他数据类型可能存在。验证类为什么要抛出异常并强制处理,而不是直接给予调用者它所需要的 - 理解该值是否确实为字符串类型?这似乎也是一个微不足道的函数,因为PHP内置了此功能。 - Mike Brant
@MikeBrant PHP确实有内置函数is_string(),我在函数中使用了它。然而,由于我使用异常处理,我不想在各处编写新异常的代码。拥有一个验证类并不罕见。我理解你所说关于类的工作方式,并且你提出了一些有价值的观点。然而,你指出的方式不是我设计的方式,虽然我的设计可能在一般情况下并不完美,但对于我正在做的事情来说,这就是我认为应该的方式。 - Matthew Herbst
@MikeBrant 同意Mike的观点,为什么一开始要这样设计呢?它检查字符串,该死的,只需返回true/false或-1 0 1(如果你更喜欢数字)。 - clockw0rk
显示剩余2条评论
4个回答

10

使用assertNull测试空的void函数:

    /**
     * @covers ../data/objects/Validate::isString
     */
    public function testIsStringNoExceptionArgumentValid() {
         $this->assertNull( Validate::isString("I am a string.") );
    }

8
在许多代码检查工具中,试图使用无返回值函数的返回值会被(正确地)标记出来。 - AndreKR

10

8
回答不错,但是应该使用expectNotToPerformAssertions而非doesNotPerformAssertions,因为前者会断言测试方法确实没有执行任何断言,而后者仅返回当前值。github链接 - Maxim Zasorin

8
我找到了一个解决方案,基于PHPUnit第2章示例2.12(example 2.12 from chapter 2 of PHPUnit)。这个方法对我来说有点hacky,但目前是我找到的最好的方法。此外,根据PHPUnit Gitub issue discussion,似乎还有其他人想要这个功能,但没有计划实现它。
testIsStringNoExceptionArgumentValid()更改为以下内容:
  /**
  * @covers ../data/objects/Validate::isString
  */
  public function testIsStringNoExceptionArgumentValid() {
    try {
      Validate::isString("I am a string.");
    } catch (InvalidArgumentException $notExpected) {
      $this->fail();
    }

    $this->assertTrue(TRUE);
  }

2
不需要try/catch那一块。也不需要fail(),异常会做那件事。 - Edson Medina
如何测试一个没有返回值且不会抛出任何异常的方法 - Souad
@Souad 要么让函数返回一些东西(你不需要在主程序逻辑中使用它!),要么创建一个本地变量,比如说“errorCode”,并且对于每个不能按预期工作的事情,将 errorCode 增加 1,这样你就可以 assertEquals(0,errorCode)。 - clockw0rk

1
如果您想测试一个无返回值的函数,只需要运行它而不进行任何断言。如果有任何问题,它将抛出异常并且测试将失败。不需要放置 $this->assertTrue(TRUE);,因为您没有运行断言,并且运行断言不是测试代码所必需的。
您将收到类似以下的消息:

Time: 7.39 seconds, Memory: 16.00 MB

OK (1 test, 0 assertions)
Process finished with exit code 0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接