zenstruck / messenger-test

Assertions and helpers for testing your symfony/messenger queues.
MIT License
218 stars 15 forks source link

Being able to grab the Message Bus #47

Closed chrisl-peopleplus closed 1 year ago

chrisl-peopleplus commented 2 years ago

Heya,

First of all thanks for the awesome tool, I'm trying to write a test to ensure that a message is dispatched by a class. The below is a simplified version of the test that uses Mockery to provide the requirements for the class

class FooDataPersisterTest extends KernelTestCase
{
    use InteractsWithMessenger;

    public function testPersist()
    {
        $entityManager = \Mockery::mock(EntityManagerInterface::class);
        $entityManager->shouldReceive('persist')
            ->once();
        $entityManager->shouldReceive('flush')
            ->once();

        $mockMessage = Mockery::mock(Hello::class);

        $messageBus = Mockery::mock(MessageBusInterface::class);
        $messageBus->shouldReceive('dispatch')
            ->once()
            ->andReturn(new Envelope($mockMessage));

        $dataPersister = new FooDataPersister($entityManager, $messageBus);
        $dataPersister->persist($entity);
    }
}

I thought I might be able to give the class the MessageBus that gets set up by TestTransport and then replace the mockery calls to the bus with something like this

class FooDataPersisterTest extends KernelTestCase
{
    use InteractsWithMessenger;

    public function testPersist()
    {
        $entityManager = \Mockery::mock(EntityManagerInterface::class);

        $entityManager->shouldReceive('persist')
            ->once();
        $entityManager->shouldReceive('flush')
            ->once();

        $dataPersister = new FooDataPersister($entityManager, $this->messenger()->bus());
        $dataPersister->persist($entity);

        $this->messenger()->dispatched()->assertCount(1);
    }
}

Can we make the message available for this use case? I'm happy to make the changes and submit a PR.

kbond commented 2 years ago

This looks similar to https://github.com/zenstruck/messenger-test/issues/41.

Can you not do something like?

$dataPersister = new FooDataPersister($entityManager, self::getContainer()->get(MessageBusInterface::class));

I thought I might be able to give the class the MessageBus that gets set up by TestTransport and then replace the mockery calls to the bus with something like this

This library does not manipulate the actual bus' in anyway, just allows swapping the transport with a test one.

chrisl-peopleplus commented 2 years ago

I can certainly see the link, using a TestMessageBus was a route I had thought about but not been down that path as yet. Thanks for the feedback, really appreciate it. I would suggest adding something into the README to suggest that people do that if they need such things.

kbond commented 2 years ago

Did my above example work for you?

For the readme, maybe something like?

This library swaps your transports with TestTransport - the bus remains the same. You can send messages to it as normal. In your tests, to access the message bus to manually send a message, use self::getContainer()->get(MessageBusInterface::class)->dispatch(new MyMessage()).

chrisl-peopleplus commented 2 years ago

I forgot to actually try it, so far have got an exception, I'll have a dig into it and update this if I find anything significant but for now...

1) App\Tests\Unit\DataPersister\ScormUnitDataPersisterTest::testNewPersist
A problem occurred in the serialization process.

/srv/api/vendor/zenstruck/assert/src/Assert/Handler/PHPUnitHandler.php:27
/srv/api/vendor/zenstruck/assert/src/Assert.php:36
/srv/api/vendor/zenstruck/assert/src/Assert.php:103
/srv/api/vendor/zenstruck/messenger-test/src/Transport/TestTransport.php:245
/srv/api/vendor/symfony/messenger/Middleware/SendMessageMiddleware.php:67
/srv/api/vendor/symfony/messenger/Middleware/FailedMessageProcessingMiddleware.php:34
/srv/api/vendor/symfony/messenger/Middleware/DispatchAfterCurrentBusMiddleware.php:68
/srv/api/vendor/symfony/messenger/Middleware/RejectRedeliveredMessageMiddleware.php:41
/srv/api/vendor/symfony/messenger/Middleware/AddBusNameStampMiddleware.php:37
/srv/api/vendor/symfony/messenger/Middleware/TraceableMiddleware.php:43
/srv/api/vendor/symfony/messenger/MessageBus.php:73
/srv/api/vendor/symfony/messenger/TraceableMessageBus.php:41
/srv/api/src/DataPersister/FooDataPersister.php:41
/srv/api/tests/Unit/DataPersister/FooDataPersisterTest.php:50
chrisl-peopleplus commented 2 years ago

To give you a bit more context, the content of the message is an object (a Doctrine entity to be exact) and the PHPSerializer::encode (line 48) is having trouble encoding it. I'll have more of a play when I can (tomorrow or in a week as I'm on holiday).

chrisl-peopleplus commented 2 years ago

So yes this works, as long as the test config understands how to serialize the message being sent. How I got around my issue above was to change the message content from an object to a simple string and I was able to make this work.

kbond commented 2 years ago

FYI, if the message isn't being serialized in production, you can disable the serialization: https://github.com/zenstruck/messenger-test#testing-serialization

chrisl-peopleplus commented 2 years ago

Didn't know about this, thanks. I think changing my messages to be more simplistic data is a good thing so finding this benefitted me :smile:

kbond commented 2 years ago

Yes, that would probably be best, switching to an async queue in the future will be easier.

amanformuli commented 1 year ago

Hi Kevin,

I tested the PR and it worked as I expected :-). You can merge the PR now.

Thanks a lot, Aman

kbond commented 1 year ago

I believe this was solved with #54.