如何确定 PHPUnit 模拟失败的原因?
How to determine why a PHPUnit mock fails?
我想在 class 之后进行单元测试。
<?php
namespace Gpx\Handlers;
use Gpx\EntityInfrastructure\Model\Events\SessionInvalidated;
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\HfxProjectionHelper;
use Gpx\HfxEventSourcing\HfxEventMetadata;
use Gpx\HfxEventSourcing\HfxRepository;
use Gpx\Hfx\MessageTransport\Response\SendableResponse;
class BroadcastSessionInvalidated implements SynchronousHandlerInterface
{
/** @var HfxRepository */
private $repository;
/** @var HfxProjectionHelper */
private $projectionHelper;
public function __construct(HfxRepository $repository, HfxProjectionHelper $projectionHelper)
{
$this->repository = $repository;
$this->projectionHelper = $projectionHelper;
}
public function handleSynchronousMessage(MessageHandlingContextInterface $context): SendableResponse
{
$content = $context->message();
$header = $context->rawMessage()->header();
$metadata = HfxEventMetadata::fromHfxHeader($header);
$payload = Payload::fromMessageContent($content);
/** @var HfxAggregateRoot $roleAggregate */
// Gpx\HfxEventSourcing\HfxAggregateRoot
$roleAggregate = $this->repository->get($payload->id());
$roleAggregate->registerEvent(SessionInvalidated::class, $payload, $metadata);
$this->repository->save($roleAggregate);
$currentEvent = $roleAggregate->currentEvent();
$context->sendNonBlockingAsynchronous('session_invalidated',$content);
$this->projectionHelper->updateReadModel();
return SendableResponse::answerTo($context->rawMessage(), 1100, [
'responseMessage' => 'Success',
'event' => $currentEvent
]);
}
}
到目前为止我写的测试用例
<?php
namespace Gpx\Tests\Feature;
use Ramsey\Uuid\Uuid;
use Gpx\Json\JsonEncode;
use Prophecy\Argument;
use PHPUnit\Framework\TestCase;
use Gpx\HfxEventSourcing\Hfx;
use Gpx\HfxEventSourcing\HfxRepository;
use Gpx\Hfx\Framework\MessageTransportApplication\Handler\MessageHandlingContextInterface;
use Gpx\Handlers\BroadcastSessionInvalidated;
use Gpx\Hfx\MessageTransport\Message\ReceivedMessage;
use Prooph\EventSourcing\AggregateRoot;
use Gpx\HfxEventSourcing\HfxAggregateRoot;
class BroadcastSessionInvalidatedTest extends TestCase
{
/** @var HfxRepository */
private $repository;
/** @var Hfx */
private $projectionHelper;
// We have to test handleSynchronousMessage handler whether it is returning sendable response with certain properties in it.
public function testHandleSynchronousMessage()
{
// Expected return value of message() function of $context
$expectedReturnValue = [
"session_id" => "1a92-4376-a8eb-deaf208e1",
"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 $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' => "8365526e-fb92-4376-a8eb-deaf208edf61",
'title' => "A task's title."
]
];
// Prophecy means prediction of the future object
// Prediction of $context object starts
$context = $this->prophesize(MessageHandlingContextInterface::class);
$context->message(Argument::any())->willReturn($expectedReturnValue);
$encodedMessage = new JsonEncode($headerResponseExpected);
$rawMessage = ReceivedMessage::fromEncodedMessage($encodedMessage->asString());
$context->rawMessage()->willReturn($rawMessage);
$context->sendNonBlockingAsynchronous('platform_session_initiated', Argument::type("array"))
->shouldBeCalled();
// Prediction of $context object ends
// Repository Mocking Starts
$ravenAggregateRoot = $this->getMockBuilder(HfxAggregateRoot::class)
->disableOriginalConstructor()
->getMock();
$this->ravenRepository = $this->prophesize(HfxRepository::class);
$this->ravenRepository->get('1a92-4376-a8eb-deaf208e1')->shouldBeCalled()->willReturn($ravenAggregateRoot);
$this->ravenRepository->save(Argument::any())->shouldBeCalled();
// Repository Mocking Ends
// Mocking Hfx and calling the method updateReadModel which will return the string UpdateReadModel
$this->projectionHelper = $this->createMock(Hfx::class);
$this->projectionHelper->method('updateReadModel')
->willReturn('UpdateReadModel');
// Actual calling
$broadcastPlatformSessionInvalidated = new BroadcastSessionInvalidated($this->ravenRepository->reveal(), $this->projectionHelper);
//$broadcastPlatformSessionInvalidated = new BroadcastSessionInvalidated($this->ravenRepository, $this->projectionHelper);
$response = $broadcastPlatformSessionInvalidated->handleSynchronousMessage($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']);
}
}
当我执行测试时它抛出一个错误
PHPUnit 6.5.4 by Sebastian Bergmann and contributors.
.E 2 / 2 (100%)
Time: 506 ms, Memory: 6.00MB
There was 1 error:
1) Gpx\Tests\Feature\BroadcastSessionInvalidatedTest::testHandleSynchronousMessage
Prophecy\Exception\Call\UnexpectedCallException: Method call:
- sendNonBlockingAsynchronous("session_invalidated", ["session_id" => "1a92-4376-a8eb-deaf208e1", "user_id" => "we", "access_jwt" => "C", "access_token" => "john@gmail.com", "refresh_token" => "C", "refresh_token_expires" => "john@gmail.com"])
on Double\MessageHandlingContextInterface\P3 was not expected, expected calls were:
- message(*)
- rawMessage()
- sendNonBlockingAsynchronous(exact("platform_session_initiated"), type(array))
/vagrant/services/sessions-stream/app/src/Gpx/Handlers/BroadcastSessionInvalidated.php:54
/vagrant/services/sessions-stream/app/tests/Feature/BroadcastPlatformSessionInvalidatedTest.php:107
ERRORS!
Tests: 2, Assertions: 6, Errors: 1.
我这里做错了什么?
Exception 告诉你哪里出了问题:你使用 shouldBeCalled()
来告诉 PHPUnit 检查方法 sendNonBlockingAsynchronous()
被调用时第一个参数 platform_session_initiated
和 array
在第二个参数.
// BroadcastSessionInvalidatedTest
$context->sendNonBlockingAsynchronous('platform_session_initiated', Argument::type("array"))
->shouldBeCalled();
满足第二个条件,但是在你的实际方法中第一个参数是session_invalidated
:
// BroadcastSessionInvalidated
$context->sendNonBlockingAsynchronous('session_invalidated',$content);
所以将期望更改为
// BroadcastSessionInvalidatedTest
$context->sendNonBlockingAsynchronous('session_invalidated', Argument::type("array"))
->shouldBeCalled();
它会起作用。
我想在 class 之后进行单元测试。
<?php
namespace Gpx\Handlers;
use Gpx\EntityInfrastructure\Model\Events\SessionInvalidated;
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\HfxProjectionHelper;
use Gpx\HfxEventSourcing\HfxEventMetadata;
use Gpx\HfxEventSourcing\HfxRepository;
use Gpx\Hfx\MessageTransport\Response\SendableResponse;
class BroadcastSessionInvalidated implements SynchronousHandlerInterface
{
/** @var HfxRepository */
private $repository;
/** @var HfxProjectionHelper */
private $projectionHelper;
public function __construct(HfxRepository $repository, HfxProjectionHelper $projectionHelper)
{
$this->repository = $repository;
$this->projectionHelper = $projectionHelper;
}
public function handleSynchronousMessage(MessageHandlingContextInterface $context): SendableResponse
{
$content = $context->message();
$header = $context->rawMessage()->header();
$metadata = HfxEventMetadata::fromHfxHeader($header);
$payload = Payload::fromMessageContent($content);
/** @var HfxAggregateRoot $roleAggregate */
// Gpx\HfxEventSourcing\HfxAggregateRoot
$roleAggregate = $this->repository->get($payload->id());
$roleAggregate->registerEvent(SessionInvalidated::class, $payload, $metadata);
$this->repository->save($roleAggregate);
$currentEvent = $roleAggregate->currentEvent();
$context->sendNonBlockingAsynchronous('session_invalidated',$content);
$this->projectionHelper->updateReadModel();
return SendableResponse::answerTo($context->rawMessage(), 1100, [
'responseMessage' => 'Success',
'event' => $currentEvent
]);
}
}
到目前为止我写的测试用例
<?php
namespace Gpx\Tests\Feature;
use Ramsey\Uuid\Uuid;
use Gpx\Json\JsonEncode;
use Prophecy\Argument;
use PHPUnit\Framework\TestCase;
use Gpx\HfxEventSourcing\Hfx;
use Gpx\HfxEventSourcing\HfxRepository;
use Gpx\Hfx\Framework\MessageTransportApplication\Handler\MessageHandlingContextInterface;
use Gpx\Handlers\BroadcastSessionInvalidated;
use Gpx\Hfx\MessageTransport\Message\ReceivedMessage;
use Prooph\EventSourcing\AggregateRoot;
use Gpx\HfxEventSourcing\HfxAggregateRoot;
class BroadcastSessionInvalidatedTest extends TestCase
{
/** @var HfxRepository */
private $repository;
/** @var Hfx */
private $projectionHelper;
// We have to test handleSynchronousMessage handler whether it is returning sendable response with certain properties in it.
public function testHandleSynchronousMessage()
{
// Expected return value of message() function of $context
$expectedReturnValue = [
"session_id" => "1a92-4376-a8eb-deaf208e1",
"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 $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' => "8365526e-fb92-4376-a8eb-deaf208edf61",
'title' => "A task's title."
]
];
// Prophecy means prediction of the future object
// Prediction of $context object starts
$context = $this->prophesize(MessageHandlingContextInterface::class);
$context->message(Argument::any())->willReturn($expectedReturnValue);
$encodedMessage = new JsonEncode($headerResponseExpected);
$rawMessage = ReceivedMessage::fromEncodedMessage($encodedMessage->asString());
$context->rawMessage()->willReturn($rawMessage);
$context->sendNonBlockingAsynchronous('platform_session_initiated', Argument::type("array"))
->shouldBeCalled();
// Prediction of $context object ends
// Repository Mocking Starts
$ravenAggregateRoot = $this->getMockBuilder(HfxAggregateRoot::class)
->disableOriginalConstructor()
->getMock();
$this->ravenRepository = $this->prophesize(HfxRepository::class);
$this->ravenRepository->get('1a92-4376-a8eb-deaf208e1')->shouldBeCalled()->willReturn($ravenAggregateRoot);
$this->ravenRepository->save(Argument::any())->shouldBeCalled();
// Repository Mocking Ends
// Mocking Hfx and calling the method updateReadModel which will return the string UpdateReadModel
$this->projectionHelper = $this->createMock(Hfx::class);
$this->projectionHelper->method('updateReadModel')
->willReturn('UpdateReadModel');
// Actual calling
$broadcastPlatformSessionInvalidated = new BroadcastSessionInvalidated($this->ravenRepository->reveal(), $this->projectionHelper);
//$broadcastPlatformSessionInvalidated = new BroadcastSessionInvalidated($this->ravenRepository, $this->projectionHelper);
$response = $broadcastPlatformSessionInvalidated->handleSynchronousMessage($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']);
}
}
当我执行测试时它抛出一个错误
PHPUnit 6.5.4 by Sebastian Bergmann and contributors.
.E 2 / 2 (100%)
Time: 506 ms, Memory: 6.00MB
There was 1 error:
1) Gpx\Tests\Feature\BroadcastSessionInvalidatedTest::testHandleSynchronousMessage
Prophecy\Exception\Call\UnexpectedCallException: Method call:
- sendNonBlockingAsynchronous("session_invalidated", ["session_id" => "1a92-4376-a8eb-deaf208e1", "user_id" => "we", "access_jwt" => "C", "access_token" => "john@gmail.com", "refresh_token" => "C", "refresh_token_expires" => "john@gmail.com"])
on Double\MessageHandlingContextInterface\P3 was not expected, expected calls were:
- message(*)
- rawMessage()
- sendNonBlockingAsynchronous(exact("platform_session_initiated"), type(array))
/vagrant/services/sessions-stream/app/src/Gpx/Handlers/BroadcastSessionInvalidated.php:54
/vagrant/services/sessions-stream/app/tests/Feature/BroadcastPlatformSessionInvalidatedTest.php:107
ERRORS!
Tests: 2, Assertions: 6, Errors: 1.
我这里做错了什么?
Exception 告诉你哪里出了问题:你使用 shouldBeCalled()
来告诉 PHPUnit 检查方法 sendNonBlockingAsynchronous()
被调用时第一个参数 platform_session_initiated
和 array
在第二个参数.
// BroadcastSessionInvalidatedTest
$context->sendNonBlockingAsynchronous('platform_session_initiated', Argument::type("array"))
->shouldBeCalled();
满足第二个条件,但是在你的实际方法中第一个参数是session_invalidated
:
// BroadcastSessionInvalidated
$context->sendNonBlockingAsynchronous('session_invalidated',$content);
所以将期望更改为
// BroadcastSessionInvalidatedTest
$context->sendNonBlockingAsynchronous('session_invalidated', Argument::type("array"))
->shouldBeCalled();
它会起作用。