28

使用 PHPUnit 测试异常时,要求每个语句或断言必须抛出异常才能通过测试的最佳方法是什么?

我基本上想做这样的事情:

public function testExceptions()
{

    $this->setExpectedException('Exception');

    foo(-1); //throws exception
    foo(1); //does not throw exception

}

//Test will fail because foo(1) did not throw an exception

我想出了以下内容,它可以完成这项工作,但在 IMO 中非常难看。

public function testExceptions()
{

    try {
        foo(-1);
    } catch (Exception $e) {
        $hit = true;
    }

    if (!isset($hit))
        $this->fail('No exception thrown');

    unset($hit);

    try {
        foo(1);
    } catch (Exception $e) {
        $hit = true;
    }

    if (!isset($hit))
        $this->fail('No exception thrown');

    unset($hit);

}
4

5 回答 5

30

我认为这是单元测试中非常常见的情况。我在这种情况下使用的方法是使用 phpunit dataProviders。一切都按预期工作,测试代码变得更加清晰和简洁。

class MyTest extends PHPUnit\Framework\TestCase
{
    public function badValues(): array
    {
       return [
           [-1],
           [1]
       ];
    }


    /**
     * @dataProvider badValues
     * @expectedException Exception
     */
    public function testFoo($badValue): void
    {
        foo($badValue);
    }
}
于 2013-08-14T09:38:56.130 回答
21

由于异常是程序流中的大事件,因此在单个测试中测试多个异常是有问题的。

最简单的事情是简单地将它分成两个测试 - 第一个需要一个异常才能通过,第二个只是运行,并且会失败它确实抛出一个。如果您愿意,您可以在第二个中添加一些其他测试(可能确认返回值),但根据它的命名,我倾向于确保它仍然只做一件基本的事情。

/**
 * @expectedException Exception
 */
public function testBadFooThrowsException()
{
    // optional, can also do it from the '@expectedException x'
    //$this->setExpectedException('Exception');
    foo(-1); //throws exception -- good.
}

public function testFooDoesNotThrowException()
{
    foo(1); //does not throw exception
}
于 2009-10-21T11:43:14.803 回答
8

稍微干净的代码(但我仍然建议拆分你的测试:

try {
    foo(-1);
    $this->fail('No exception thrown');
} catch (Exception $e) {}
于 2013-05-23T10:10:28.413 回答
1

这对我来说没有意义。

我猜您正在尝试使用一个测试用例来测试多个单独的事物,这是一种不好的做法。

foo()抛出预期的异常时,测试用例成功并且bar()不会运行。

只需创建两个单独的测试用例,这比您在第二个清单中生成的代码要少得多。

bar()或者解释为什么在异常失败后运行foo()时也会抛出异常是有意义的。

于 2009-10-20T11:09:56.883 回答
1

扩展@dave1010 的答案,这是我解决这个问题的方法。这使您可以在一次测试中保持所有这些“断言”整洁。您只需定义一个应该无法通过测试的变量数组,然后遍历每个变量并查看是否引发异常。如果有任何失败(不抛出异常),则测试失败,否则测试通过。

<?php

public function testSetInvalidVariableType()
{
    $invalid_vars = array(
        '',                 // Strings
        array(),            // Arrays
        true,               // Booleans
        1,                  // Integers
        new \StdClass       // Objects
    );

    foreach ($invalid_vars as $var) {
        try {
            $object->method($var);
            $this->fail('No exception thrown for variable type "' . gettype($var) . '".');
        } catch (\Exception $expected) {
        }
    }
}
于 2013-07-12T14:59:57.570 回答