unit-testingmezzioprophecy

How to make a Test Double for the RequestHandlerInterface class in Zend Expressive using Prophesy?


I'm trying to make a unit test for the process() method of a middleware in a Zend Expressive application. To do this, I need to mock the out the $delegate parameter for the method which is of type RequestHandlerInterface and will have the method handle().

This should be pretty easy to do as I've mocked successfully with Prophesy on other objects in this test:

Whenever the handle() method gets called, I receive the following error: "Unexpected method call on Double\RequestHandlerInterface\P18:\n - handle(\n Double\ServerRequestInterface\P17:000000004a01de0d000000000617c05e Object (\n 'objectProphecy' => Prophecy\Prophecy\ObjectProphecy Object (*Prophecy*)\n )\n )\nexpected calls were:\n - handle(\n\n )"

Here's the test. Note that other mocks are working as expected but the handle() method on the $mockDelegate still throws the error when it is called:

/**
 * @test
 */
public function 
testReturnsRedirectResponseForHandlerWhenNoErrorsFoundRequestTypePOST()
{

    $renderer = $this->prophesize(TemplateRendererInterface::class);
    $renderer
        ->render('app::contract-form-page', [])
        ->willReturn('');

    $validateSubmitAction = new ValidateSubmitAction(
        $this->router->reveal(),
        $renderer->reveal(),
        get_class($this->container->reveal()),
        $this->logger->reveal()
    );

    $mockRequest = $this->prophesize(ServerRequestInterface::class);
    $mockRequest->getMethod()->willReturn('POST');
    $mockRequest->getBody()->willReturn(
    //create fake object with getContents method
        new class {
            public function getContents(){ return 'location-number=testLoc&contract-number=1234';}
        });

    $mockDelegate = $this->prophesize(RequestHandlerInterface::class);
    $mockDelegate->handle()->willReturn('');

    $response = $validateSubmitAction->process(
        $mockRequest->reveal(),
        $mockDelegate->reveal()
    );

    $this->assertInstanceOf(ValidateSubmitAction::class, $validateSubmitAction);
}

Here's the method it is trying to test. The error seems to occur when the method is supposed to delegate the request down the pipeline. See here:

public function process(ServerRequestInterface $request, RequestHandlerInterface $delegate): ResponseInterface
{
    ...
    // Delegate on to the handler
    return $delegate->handle($request); //<-- this is where the error occurs in the unit test

How can I accurately mock the RequestHandlerInterface handle() method with Prophesy so as to achieve an error-free test?


Solution

  • You have this: $mockDelegate->handle()->willReturn('');, but it should be something like this this:

    $handler->handle(Argument::that([$mockRequest, 'reveal']))->willReturn('');

    In your code you expect handle() to be called without any arguments. But it's called with an instance of the mocked request interface.

    Have a look at an example from zend-expressive-session:

    public function testMiddlewareCreatesLazySessionAndPassesItToDelegateAndPersistsSessionInResponse()
    {
        $request = $this->prophesize(ServerRequestInterface::class);
        $request
            ->withAttribute(SessionMiddleware::SESSION_ATTRIBUTE, Argument::type(LazySession::class))
            ->will([$request, 'reveal']);
    
        $response = $this->prophesize(ResponseInterface::class);
    
        $handler = $this->prophesize(RequestHandlerInterface::class);
        $handler->handle(Argument::that([$request, 'reveal']))->will([$response, 'reveal']);
    
        $persistence = $this->prophesize(SessionPersistenceInterface::class);
        $persistence
            ->persistSession(
                Argument::that(function ($session) use ($persistence, $request) {
                    $this->assertInstanceOf(LazySession::class, $session);
                    $this->assertAttributeSame($persistence->reveal(), 'persistence', $session);
                    $this->assertAttributeSame($request->reveal(), 'request', $session);
                    return $session;
                }),
                Argument::that([$response, 'reveal'])
            )
            ->will([$response, 'reveal']);
    
        $middleware = new SessionMiddleware($persistence->reveal());
        $this->assertSame($response->reveal(), $middleware->process($request->reveal(), $handler->reveal()));
    }