PHP 单元故障测试

PHP unit failure testing

我必须测试此 class 的失败条件

<?php
namespace Gpx\Handlers;
use Gpx\EntityInfrastructure\Model\Events\PlatformSessionInitiated;
use Gpx\EntityInfrastructure\Model\Payload;
use Gpx\Hfx\Framework\MessageTransportApplication\Handler\SynchronousHandlerInterface;
use Gpx\Hfx\Framework\MessageTransportApplication\Handler\MessageHandlingContextInterface;
use Gpx\HfxEventSourcing\HfxAggregateRoot;
use Gpx\HfxEventSourcing\HfxEventMetadata;
use Gpx\HfxEventSourcing\HfxProjectionHelper;
use Gpx\HfxEventSourcing\HfxRepository;
use Gpx\Hfx\MessageTransport\Response\SendableResponse;
use Gpx\Exceptions\IdNotDefinedException;
use Gpx\Exceptions\AggregateNotFoundException;

class BroadcastPlatformSessionInitiated implements SynchronousHandlerInterface
{

    /** @var HfxRepository */
    private $repository;

    /** @var  HfxProjectionHelper */
    private $projectionHelper;


    public function __construct(HfxRepository $repository, HfxProjectionHelper $projectionHelper)
    {
        $this->repository = $repository;
        $this->projectionHelper = $projectionHelper;
    }

    /*
     * @param MessageHandlingContextInterface $context
     * @return SendableResponse
     */
    public function handleSynchronousMessage(MessageHandlingContextInterface $context): SendableResponse
    {
        try {
            $content = $context->message();
            $header = $context->rawMessage()->header();

            $metadata = HfxEventMetadata::fromHfxHeader($header);
            $payload = Payload::fromMessageContent($content);

            $roleAggregate = HfxAggregateRoot::createEntityFromEvent(PlatformSessionInitiated::class, $payload, $metadata);
            $this->repository->save($roleAggregate);

            $currentEvent = $roleAggregate->currentEvent();
            $context->sendNonBlockingAsynchronous('platform_session_initiated', $currentEvent);

            $this->projectionHelper->updateReadModel(HfxAggregateRoot::class);

            return SendableResponse::answerTo($context->rawMessage(), 1000, [
                'responseMessage' => 'Success',
                'event' => $currentEvent
            ]);
        } catch (IdNotDefinedException $e) {
            return SendableResponse::answerTo($context->rawMessage(), 2000, [
                'responseMessage' => 'Failure. Session Id is not defined.'
            ]);
        }
    }
}

以下是我写的测试用例

<?php

namespace Gpx\Tests\Handlers;

use Ramsey\Uuid\Uuid;
use Gpx\Json\JsonEncode;
use Prophecy\Argument;
use PHPUnit\Framework\TestCase;
use Gpx\HfxEventSourcing\HfxProjectionHelper;
use Gpx\HfxEventSourcing\HfxRepository;
use Gpx\Hfx\Framework\MessageTransportApplication\Handler\MessageHandlingContextInterface;
use Gpx\Handlers\BroadcastPlatformSessionInitiated;
use Gpx\Hfx\MessageTransport\Message\ReceivedMessage;
use Gpx\Exceptions\IdNotDefinedException;

class BroadcastPlatformSessionInitiatedTest extends TestCase
{
    /** @var HfxRepository */
    private $repository;

    /** @var  HfxProjectionHelper */
    private $projectionHelper;

    /** @var  MessageHandlingContext */
    private $context;

    /**
     * This will run before each test
     */
    public function setUp()
    {

        // Expected return value of message() function of $this->context
        $expectedReturnValue = [
            "session_id" => "1a92-4376-a8eb-deaf208ssess11",
            "user_id" => "we",
            "access_jwt" => "C",
            "access_token" => "john@gmail.com",
            "refresh_token" => "C",
            "refresh_token_expires" => "john@gmail.com"
        ];

        // Expected return value of rawMessage() function of $this->context
        $headerResponseExpected = [
            'header' => [
                'version' => '2.0',
                'originId' => (string)Uuid::uuid4(),
                'destination' => 'application/meta@1.0.0',
                'sent' => '2017-12-19T10:12:37.941+00:00'
            ],
            'content' => [
                'session_id' => null,
                'title' => "A task's title."
            ]
        ];

        // Prediction of $this->context object starts
        $this->context = $this->prophesize(MessageHandlingContextInterface::class);
        $this->context->message(Argument::any())->willReturn($expectedReturnValue);

        $encodedMessage = new JsonEncode($headerResponseExpected);
        $rawMessage = ReceivedMessage::fromEncodedMessage($encodedMessage->asString());
        $this->context->rawMessage()->willReturn($rawMessage);

        $this->context->sendNonBlockingAsynchronous('platform_session_initiated', Argument::type("array"))
            ->shouldBeCalled();
        // Prediction of $this->context object ends
    }

    // We have to test handleSynchronousMessage handler whether it is returning sendable response with certain properties in it.
    public function testHandleSynchronousMessageForSuccess()
    {

        // Prophecy means prediction of the future object
        $this->ravenRepository = $this->prophesize(HfxRepository::class);
        $this->ravenRepository->save(Argument::any())
            ->shouldBeCalled();

        // Mocking HfxProjectionHelper and calling the method updateReadModel which will return the string UpdateReadModel
        $this->projectionHelper = $this->createMock(HfxProjectionHelper::class);
        $this->projectionHelper->method('updateReadModel')
            ->willReturn('UpdateReadModel');

        // Actual calling
        $broadcastPlatformSessionInitiated = new BroadcastPlatformSessionInitiated($this->ravenRepository->reveal(), $this->projectionHelper);
        $response = $broadcastPlatformSessionInitiated->handleSynchronousMessage($this->context->reveal());

        $this->assertInstanceOf('Gpx\Hfx\MessageTransport\Response\SendableResponse', $response);
        $this->assertArrayHasKey("responseMessage", $response->content()->data());
        $this->assertArrayHasKey("event", $response->content()->data());
        $this->assertEquals("Success", $response->content()->data()['responseMessage']);

    }

    // We have to test handleSynchronousMessage handler whether it is returning sendable response with certain properties in it.
    public function testHandleSynchronousMessageForFailure()
    {

        // Expected return value of message() function of $this->context
        $expectedReturnValue = [
            "session_id" => null,
            "user_id" => "we",
            "access_jwt" => "C",
            "access_token" => "john@gmail.com",
            "refresh_token" => "C",
            "refresh_token_expires" => "john@gmail.com"
        ];

        // Expected return value of rawMessage() function of $this->context
        $headerResponseExpected = [
            'header' => [
                'version' => '2.0',
                'originId' => (string)Uuid::uuid4(),
                'destination' => 'application/meta@1.0.0',
                'sent' => '2017-12-19T10:12:37.941+00:00'
            ],
            'content' => [
                'session_id' => '1a92-4376-a8eb-deaf208ssess11',
                'title' => "A task's title."
            ]
        ];

        // Prediction of $this->context object starts
        $this->context = $this->prophesize(MessageHandlingContextInterface::class);
        $this->context->message(Argument::any())->willReturn($expectedReturnValue);

        $encodedMessage = new JsonEncode($headerResponseExpected);
        $rawMessage = ReceivedMessage::fromEncodedMessage($encodedMessage->asString());
        $this->context->rawMessage()->willReturn($rawMessage);

        $this->context->sendNonBlockingAsynchronous('platform_session_initiated', Argument::type("array"))->shouldNotBeCalled();
        // Prediction of $this->context object ends

        // Prophecy means prediction of the future object
        $this->ravenRepository = $this->prophesize(HfxRepository::class);


        // Mocking HfxProjectionHelper and calling the method updateReadModel which will return the string UpdateReadModel
        $this->projectionHelper = $this->createMock(HfxProjectionHelper::class);
        $this->projectionHelper->method('updateReadModel')->willReturn('UpdateReadModel');

        // Actual calling
        $broadcastPlatformSessionInitiated = new BroadcastPlatformSessionInitiated($this->ravenRepository->reveal(), $this->projectionHelper);
        $response = $broadcastPlatformSessionInitiated->handleSynchronousMessage($this->context->reveal());

        $this->assertInstanceOf('Gpx\Hfx\MessageTransport\Response\SendableResponse', $response);
        $this->assertArrayHasKey("responseMessage", $response->content()->data());
        $this->assertEquals("Failure. Session Id is not defined.", $response->content()->data()['responseMessage']);

    }
}

这是我遇到的 testHandleSynchronousMessageForFailure 失败

Gpx\Tests\Handlers\BroadcastPlatformSessionInitiatedTest::testHandleSynchronousMessageForFailure
Some predictions failed:
  Double\MessageHandlingContextInterface\P3:
    No calls have been made that match:
      Double\MessageHandlingContextInterface\P3->sendNonBlockingAsynchronous(exact("platform_session_initiated"), type(array))
    but expected at least one.

FAILURES!
Tests: 3, Assertions: 18, Failures: 1.

任何人都可以澄清我在这里做错了什么吗?

你的 setUp() 方法在 每个 测试之前被调用,所以 testHandleSynchronousMessageForFailure() 也期待 sendNonBlockingAsynchronous() 被调用:

$this->context->sendNonBlockingAsynchronous('platform_session_initiated', Argument::type("array"))
        ->shouldBeCalled();

即使你在失败测试中调用shouldNotBeCalled()。因此,将 shouldBeCalled() 调用移动到 testHandleSynchronousMessageForSuccess(),这样它会期望在成功测试中调用它,而 not 在失败时调用测试。

您还应该告诉 PHPUnit 在失败测试中期望 IdNotDefinedException

$this->expectException(Gpx\Exceptions\IdNotDefinedException::class);