phpunit-testingphpunitprophecy

PHP unit failure testing


I have to test this class for failure condition

<?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.'
            ]);
        }
    }
}

Following is the test case I have written

<?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']);

    }
}

This is the failure I am getting for 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.

Could any one please clarify what I am doing wrong here?


Solution

  • Your setUp() method gets called before every test, so testHandleSynchronousMessageForFailure() is also expecting sendNonBlockingAsynchronous() to be called:

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

    Even if you call shouldNotBeCalled() on it in the failure test. So, move the shouldBeCalled() call to the testHandleSynchronousMessageForSuccess(), that way it will expect it to be called in the success test, and not to be called in the failure test.

    You should also tell PHPUnit to expect an IdNotDefinedException in the failure test:

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