4

Seems difficult and there isn't much documentation around this (I'm using FirendsOfBehat Symfony extension). I want to test whether Transport carries any events by using get() method but I'm not getting any results. It feels like its not routing the correct Bus.

declare(strict_types=1);

namespace App\Features\BehatContext;

class MessengerContext implements Context
{
    /**
     * @var TransportInterface
     */
    private $transport;

    /**
     * MessengerContext constructor.
     *
     * @param TransportInterface $transport ??? Is this ok
     */
    public function __construct(TransportInterface $transport)
    {
        // Symfony\Component\Messenger\Transport\InMemoryTransport
        $this->transport = $transport;
    }

    /**
     * THIS IS WHAT DOESN'T WORK
     * @Given /^(\d+) Events? "([^"]*)" is dispatched$/
     */
    public function eventAEventIsDispatched()
    {
        $eventsDispatched = $this->transport->get();
        Assert::assertTrue(count($eventsDispatched));
    }
}

My packages/messenger.yaml configuration:

framework:
    messenger:
        default_bus: event.bus
        buses:
            command.bus:
                middleware:
                    - validation
            event.bus:
                default_middleware: allow_no_handlers

        transports:
             sync: 'sync://'
             event: 'in-memory:///'

        routing:
             'App\AddMagazine': sync
             'App\MagazineAdded': event
             'App\EventAdapter': event

This is the class that dispatches my events

declare(strict_types=1);

namespace App\Event\Dispatcher;


class SymfonyEventDispatcher implements ApplicationDomainDispatcherInterface
{
    private $messageBus;

    /**
     * SymfonyEventDispatcher constructor.
     *
     * @param MessageBusInterface $sfDispatcher
     */
    public function __construct(MessageBusInterface $eventBus)
    {
        // messageBus is Symfony\Component\Messenger\TraceableMessageBus
        $this->messageBus = $eventBus;
    }

    /**
     * @param EventInterface $event
     *
     * @return EventInterface
     */
    public function dispatch(EventInterface $event): EventInterface
    {
        $eventAdapter = new EventAdapter($event);
        $this->messageBus->dispatch(new Envelope($eventAdapter));

        return $eventAdapter;
    }
}

This my service_test.yaml file which is taken into account when running Behat tests:

services:

    _defaults:
        autowire: true
        autoconfigure: true

    App\Features\BehatContext\:
        resource: '../features/BehatContext/*'

    App\Features\BehatContext\MessengerContext:
        arguments:
            $transport: '@messenger.transport.event'

Checking my logs I can see that the messenger did send the event:

[2019-08-30 14:14:50] messenger.INFO: Sending message App\EventAdapter with Symfony\Component\Messenger\Transport\InMemoryTransport {"message":"[object] (App\EventAdapter: {})","class":"App\EventAdapter","sender":"Symfony\Component\Messenger\Transport\InMemoryTransport"} []

yivi
  • 42,438
  • 18
  • 116
  • 138
db306
  • 934
  • 11
  • 22

2 Answers2

2

Here's an up to date example for anyone stumbling across this using the when also using the MinkContext

<?php

declare(strict_types=1);

namespace App\Features\Bootstrap;

use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\MinkExtension\Context\MinkContext;
use PHPUnit\Framework\Assert;
use Psr\Container\ContainerInterface;
use Symfony\Component\Messenger\Transport\InMemoryTransport;

/**
 * @author Daniel West <daniel@silverback.is>
 */
class MessengerContext implements Context
{
    private ContainerInterface $container;

    /**
     * @BeforeScenario
     */
    public function getContexts(BeforeScenarioScope $scope): void
    {
        /** @var MinkContext $mink */
        $mink = $scope->getEnvironment()->getContext(MinkContext::class);
        $client = $mink->getSession()->getDriver()->getClient();
        $container = $client->getContainer();
        $this->container = $container->has('test.service_container') ? $container->get('test.service_container') : $container;
    }

    /**
     * @Then /(\d+) message(?:s)? should have been sent to the transport named (.*)/
     */
    public function messaesHaveBeenSentToTheTransport(int $count, string $name): void
    {
        /** @var InMemoryTransport $transport */
        $transport = $this->container->get(sprintf('messenger.transport.%s', $name));
        Assert::assertCount($count, $transport->get());
    }
}
Daniel West
  • 154
  • 10
  • With adaptation you could use also the library https://github.com/zenstruck/messenger-test. Your code base is a good start. Thank you :) – terox Dec 09 '22 at 11:41
1

I think that the problem is that there are two different containers (one for the app and another for Mink isolated driver), and they are not sharing their services state, so it's impossible to get the in-memory enqueued messages.

The first solution that comes to me is renouncing to use the Mink API and start using the BrowserKit API (https://github.com/FriendsOfBehat/SymfonyExtension/pull/82)

The second solution would be using this recently introduced feature for accessing tested application services via the driver's service container: https://github.com/FriendsOfBehat/SymfonyExtension/pull/116

Another common problem comes from making multiple requests during the same scenario, then all the messages stored in the memory will get lost. In that case, I recommend using the doctrine transport.

Nove
  • 165
  • 2
  • 8