在PHPUnit中使用expectException和间谍

时间:2016-05-03 22:46:37

标签: php unit-testing phpunit spy test-double

我正在使用PHPUnit ~5.2和PHP ~7.0.0

我有一个包装存储库的类,捕获它抛出的异常,调用记录器然后重新抛出异常。

public function storeDonation( Donation $donation ) {
    try {
        $this->repository->storeDonation( $donation );
    }
    catch ( StoreDonationException $ex ) {
        $this->logger->log( $this->logLevel, $ex->getMessage(), [ 'exception' => $ex ] );
        throw $ex;
    }
}

在我的测试中,我为记录器创建了一个间谍,所以我可以看到哪些调用记录功能。这个间谍是一个专门的测试双未通过PHPUnit模拟API创建。使用PHPUnit模拟API时,我遇到的问题不会发生,但是我不会在这里询问有关该决定的反馈。

我遇到问题的测试方法有一些设置代码,然后调用PHPUnits expectException(以前在PHPUnit 5.1及更早版本中为setExpectedException),然后调用生产方法,最后从间谍那里获取日志记录调用,以便在正确的位置断言。

public function testWhenGetDonationByIdThrowException_itIsLoggedAndThrown() {
    $logger = new LoggerSpy();

    $loggingRepo = new LoggingDonationRepository(
        $this->newThrowingRepository(),
        $logger
    );

    $this->expectException( GetDonationException::class );
    $loggingRepo->getDonationById( 1337 );

    $this->assertEquals(
        [
            [
                LogLevel::CRITICAL,
                self::GET_DONATION_BY_ID_EXCEPTION_MESSAGE,
                $this->newGetDonationException()
            ]
        ],
        $logger->getLogCalls()
    );
}

问题是当抛出异常时,PHPUnit捕获它并评估expectException,之后我的测试方法的执行不会继续。因此使用我的间谍的断言永远不会运行。

我正在寻找解决这个问题的方法。我已经考虑将方法拆分为具有expectException部分的方法,而另一个方法同时具有生产代码调用的伪try-catch和spy的断言。有没有更好方法的建议?

1 个答案:

答案 0 :(得分:3)

当您抛出异常时,以下所有代码都会被中断,直到catch语句。这也适用于您的测试方法。

PHPUnit总是捕获异常以便它可以记录它们,并且通过使用expectedException,您只需告诉它处理抛出稍有不同的异常。

所以是的,如果你想在抛出异常后想要进行其他检查,你需要编写自己的try catch块。以下是您如何做到这一点的示例:

public function testWhenGetDonationByIdThrowException_itIsLoggedAndThrown() {
    $logger = new LoggerSpy();

    $loggingRepo = new LoggingDonationRepository(
        $this->newThrowingRepository(),
        $logger
    );

    try {
        $loggingRepo->getDonationById( 1337 );
    } catch (GetDonationException $e) {
        $this->assertEquals(
            [[
                LogLevel::CRITICAL,
                self::GET_DONATION_BY_ID_EXCEPTION_MESSAGE,
                $this->newGetDonationException()
            ]],
            $loger->getLogCalls()
        );

        return;
    }

    $this->fail('Expected exception wasn\'t thrown!');
}