测试可能 return 异常的方法的正确方法

Correct way to test methods that may return exceptions

我正在测试一个可以做三件事的 User::validateAdmin() 模型方法:

到目前为止我有这个:

$result = $this->User->validateAdmin($validAdmin);
$espected = true;
$this->assertEquals($result, $espected);

$result = $this->User->validateAdmin($disabledAdmin);
$espected = false;
$this->assertEquals($result, $espected);

$this->setExpectedException('NotAnAdminException');
$result = $this->User->validateAdmin($anotherUserRole);
$espected = null;
$this->assertEquals($result, $espected);
$this->setExpectedException(null);

...但这使得 PHPUnit 忽略所有后续的 NotAnAdminException 用法,无论预期与否。

正确测试我的三个场景的正确方法是什么?

正如 PHPUnit doesn't continue test after expecting an exception 所解释的那样,问题中显示的方法存在缺陷,因为 PHPUnit 将测试包装在常规 try/catch 块中,因此一旦抛出异常,其余断言就会在测试方法被忽略。除此之外,\PHPUnit_Framework_TestCase::setExpectedException 方法按预期工作:当且仅当抛出给定异常时测试通过。

最干净的方法是在不同的方法中拆分断言:

public function testvalidateAdminValid(){
    $validAdmin = ........;
    $result = $this->User->validateAdmin($validAdmin);
    $espected = true;
    $this->assertEquals($result, $espected);
}

public function testvalidateAdminExpired(){
    $disabledAdmin = ........;
    $result = $this->User->validateAdmin($disabledAdmin);
    $espected = false;
    $this->assertEquals($result, $espected);
}

public function testvalidateAdminNotAnAdmin(){
    $anotherUserRole = ........;
    $this->setExpectedException('NotAnAdminException');
    $this->User->validateAdmin($anotherUserRole);
}

如果您希望将所有相关测试都放在同一个方法中,您必须自己模拟 setExpectedException,例如:

try{
    $result = $this->User->validateAdmin($anotherUserRole);
    $this->fail('Failed to throw NotAnAdminException');
}catch(NotAnAdminException $e){
    $this->assertEmpty($result);
}

您应该使测试简短明了。所以你绝对应该将你的测试分成多个测试来测试你的方法的不同方面。

我也鼓励您以 BDD 风格编写测试。所以你应该测试预期的行为,例如:

public function shouldReturnTrueForAdmin() {
    //given
    $validAdmin = ........;

    //when
    $result = $this->User->validateAdmin($validAdmin);

    //then
    $this->assertTrue($result);
}

在 PhpUnit 中很难用这种方式测试异常。

你可以试试CatchException from ouzo goodies:

public function shouldFailIfNotAdmin() {
    //given
    $anotherUserRole = ........;

    //when
    CatchException::when($this->User)>validateAdmin($anotherUserRole);

    //then
    CatchException::assertThat()->isInstanceOf("NotAnAdminException");
}