2009-10-20 125 views
17

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

我基本上想要做的是这样的:我来了;下面,做这项工作,但相当难看IMO

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 

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); 

} 

回答

16

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

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

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

我确实看到你的观点,尽管当每个测试的要点确保引发异常时,仍然感觉有多个测试有点奇怪。 – etheros 2009-10-21 14:13:57

+3

您还可以使用'@dataProvider注释'来传入值(甚至是预期的异常的名称 - 用'$ this-> setExpectedException($ x)')。添加一个新的测试值(这将引发一个异常)只会是dataProvider函数中的另一个数组项。 – 2011-03-18 11:06:48

+1

罗伯特马丁会说总是将病例分成不同的测试。在我看来,@ AlisterBulman答案显示了该问题的完美解决方案。 – 2013-01-08 17:35:21

1

这对我没有意义。

我想你试图用一个测试用例来测试多个单独的东西,这是不好的做法。

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

只是创建两个单独的测试用例,这是很少的代码,那么你在第二个列表中产生的。

或者解释为什么在foo()失败并发生异常之后运行bar()有意义时,它也会抛出异常。

+0

我试图用不同的参数调用相同的函数(并且我编辑了我的示例以更好地反映这一点)。 PHPUnit手册中的示例显示了在单个测试中创建了多个相关的断言,所以我试图复制这个,但有例外。 – etheros 2009-10-20 12:49:22

6

稍微干净的代码(但我还是建议分割你的测试:

try { 
    foo(-1); 
    $this->fail('No exception thrown'); 
} catch (Exception $e) {} 
0

扩大对@ 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) { 
     } 
    } 
} 
+0

'失败'抛出'PHPUnit_Framework_AssertionFailedError'。您需要在代码中使用自定义例外。 – sectus 2016-10-05 05:13:50

15

我认为这是单元测试中很常见的情况。我在这种情况下使用的方法是使用phpunit dataProviders。所有工作都如预期的那样,并且测试代码变得更加清晰和简洁。

class MyTest extends PHPUnit_Framework_TestCase 
{ 
    public function badValues() 
    { 
     return array(
      array(-1), 
      array(1) 
     ); 
    } 


    /** 
    * @dataProvider badValues 
    * @expectedException Exception 
    */ 
    public function testFoo($badValue) 
    { 
     foo($badValue); 
    } 
} 
+2

非常好的想法...用一个测试测试许多例外。 – Andrew 2015-06-25 20:12:17

+0

'array(1)'应该在那里吗? – 2016-01-27 03:10:06

+3

该解决方案是最美丽和优雅,必须是被接受的答案。 – 2016-04-29 06:17:21

相关问题