PHPUnit:我期待异常,它被抛出但“$this->expectException”不起作用

PHPUnit: I expect exception, it's being thrown but "$this->expectException" doesn't work

我想从我的 Laravel 服务 class 中测试一个特定的方法。当我测试“一个好的案例”时,我所有的断言都是绿色的并且一切都通过了,但是我想测试一个边缘案例,在某些情况下这个方法会抛出异常。

当我准备我的数据和参数来获得这个异常时,它确实被抛出但我不能$this->expectException()

我要测试的服务有 4 个依赖项:

class TechMed3dSizeMeService extends AbstractService implements TechMed3dSizeMeServiceInterface
{
    private DatabaseManager $databaseManager;
    private FileSystem $fileSystem;
    private OrderServiceInterface $orderService;
    private ScanServiceInterface $scanService;

    public function __construct(
        DatabaseManager $databaseManager,
        FileSystem $fileSystem,
        OrderServiceInterface $orderService,
        ScanServiceInterface $scanService
    ) {
        $this->databaseManager = $databaseManager;
        $this->fileSystem = $fileSystem;
        $this->orderService = $orderService;
        $this->scanService = $scanService;
    }

在我的测试中,在 setUp() 我嘲笑他们:

        $this->databaseManager = $this->getMockBuilder(DatabaseManager::class)
            ->disableOriginalConstructor()
            ->getMock();

        $this->fileSystem = $this->getMockBuilder(FileSystem::class)
            ->disableOriginalConstructor()
            ->getMock();

        $this->orderService = $this->getMockBuilder(OrderServiceInterface::class)
            ->disableOriginalConstructor()
            ->getMock();

        $this->scanService = $this->getMockBuilder(ScanServiceInterface::class)
            ->disableOriginalConstructor()
            ->getMock();

        $this->techMed3dSizeMeService = new TechMed3dSizeMeService(
            $this->databaseManager,
            $this->fileSystem,
            $this->orderService,
            $this->scanService,
        );

如您所见,我要测试的服务未被模拟,但我只是创建了它的新实例 $this->techMed3dSizeMeService = new TechMed3dSizeMeService

我要测试的方法是:

    public function getUrlScheme(int $orderId): string
    {
        $order = $this->orderService->getOrderById($orderId);

        if (!$order instanceof Order) {
            throw new ServiceResourceNotFoundException(); // I want to get this exception in assertion
        }

        if (!$this->canAccessByOwnershipOrSufficientRole($order)) {
            throw new ServiceAuthorizationException();
        }

        if (!$order->canObtainScansFromExternalApp) {
            throw new OrderNotReadyToObtainScansFromExternalAppException(
                'Requested Order did not pass through required steps to obtain scans.'
            );
        }

        $orderDetails = $order->orderDetailsOrders;

        if (!$orderDetails instanceof OrderDetails) {
            throw new OrderDoesNotHaveOrderDetails(
                'Requested Order does not have Order Details necessary to obtain scans.'
            );
        }

        return sprintf(
            'tm3d-in-3dsizeme://data?params=%s',
            $this->generatePayloadForUrlScheme($order, $orderDetails)
        );
    }

好案例通过:

    public function get_url_scheme_for_given_order_owned_by_prosthetist()
    {
        // Act as
        $this->actingAs($this->userProsthetist);

        // Create necessary data
        $order = $this->createOrderThanCanObtainScansForUser($this->userProsthetist);
        $this->createOrderDetailsForOrder($order);

        // Invoke
        $this->orderService->expects($this->once())
            ->method('getOrderById')
            ->will($this->returnValue($order));

        // Assert
        $urlScheme = $this->techMed3dSizeMeService->getUrlScheme($order->id);

        $this->assertTrue(Str::startsWith($urlScheme, 'tm3d-in-3dsizeme://data?params='));
        $this->assertTrue(Str::endsWith($urlScheme, '='));
    }

对于糟糕的情况,我提供了不存在的订单 ID,我希望得到异常:

    public function get_url_scheme_for_given_order_owned_by_prosthetist_where_order_does_not_exist()
    {
        // Act as
        $this->actingAs($this->userProsthetist);

        // Invoke
        $this->orderService->expects($this->once())
            ->method('getOrderById')
            ->will($this->returnValue(null));

        // Assert
        $urlScheme = $this->techMed3dSizeMeService->getUrlScheme(999999); // I provide non existing ID

        $this->expectException(ServiceResourceNotFoundException::class); // I want to get this and I get but this doesn't work
    }

结果:

bash-5.0# ./vendor/bin/phpunit --filter=TechMed3dSizeMeServiceTest
PHPUnit 8.5.13 by Sebastian Bergmann and contributors.

.E                                                                  2 / 2 (100%)

Time: 5.15 seconds, Memory: 28.00 MB

There was 1 error:

1) Tests\Unit\Services\TechMed3dSizeMeService\TechMed3dSizeMeServiceTest::get_url_scheme_for_given_order_owned_by_prosthetist_where_order_does_not_exist
App\Exceptions\Services\ServiceResourceNotFoundException:

/app/app/Services/TechMed3dSizeMeService/TechMed3dSizeMeService.php:45
/app/tests/Unit/Services/TechMed3dSizeMeService/TechMed3dSizeMeServiceTest.php:53

ERRORS!
Tests: 2, Assertions: 3, Errors: 1.
bash-5.0#

看起来异常被抛出但没有被 PHPUnit 拾取?我究竟做错了什么?我不应该使用 new?

只需将 $this->expectException(ServiceResourceNotFoundException::class); 移动到 $urlScheme = $this->techMed3dSizeMeService->getUrlScheme(999999);

之前
public function get_url_scheme_for_given_order_owned_by_prosthetist_where_order_does_not_exist()
{
    // Act as
    $this->actingAs($this->userProsthetist);

    // Invoke
    $this->orderService->expects($this->once())
        ->method('getOrderById')
        ->will($this->returnValue(null));


    // Assert
    $this->expectException(ServiceResourceNotFoundException::class);
    $urlScheme = $this->techMed3dSizeMeService->getUrlScheme(999999); // I provide non existing ID
}