Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Guide "How to mock clients in tests" #177

Open
gregurco opened this issue Jan 4, 2018 · 11 comments
Open

Guide "How to mock clients in tests" #177

gregurco opened this issue Jan 4, 2018 · 11 comments

Comments

@gregurco
Copy link
Member

gregurco commented Jan 4, 2018

Write a guide how to mock clients in functional tests.

@Neirda24
Copy link
Contributor

@gregurco : Here is how I did it: https://gist.github.com/Neirda24/d662dda169518dc75a5beb924c4182c2 tell me what you think

@rrajkomar
Copy link
Contributor

rrajkomar commented Sep 28, 2018

Any updates on this topic. This is definitely a prerquisite for this bundle to be considered usable in a production system.

@gregurco
Copy link
Member Author

I plan to write it as soon as will finish with guide for plugins. It takes some time.

@bropp
Copy link

bropp commented Aug 13, 2019

Any thoughts about how to properly provide mock clients in functional tests?

I've been able to configure the bundle to use the MockHandler as described, but don't know how to properly provide the mocked responses in my tests -- the MockHandler queue is always empty.

@ggagosh
Copy link

ggagosh commented Aug 14, 2019

Any news about it?

As we use this as our one of the main bundle need some documentation (examples is great too) about how to mock it the right way.

@gregurco
Copy link
Member Author

@bropp @ggagosh there are a lot of methods how to mock client. Each method is better in special cases. Could you please provide more details on your requirements from your projects and I will try to provide more information. Probably based on these answers we will be able to create documentation page.

@matheusfaustino
Copy link

Hi, first of all, thank you for your lib. I'd like to know if there is a example of how to do the mock. I replaced the handler, it's the MockHandler, but I can't add any request to the queue in the tests. I tried the setHandler method from Guzzle Client and I tried to use reflection class to get the Mock class and append things, but without success.

Could somebody help me with this, please? Thnx in advance

@matheusfaustino
Copy link

Well, I think I found a way through. One thing that I didn't mention before it was that I'm using api-platform and because I'm using api-platform, I'm using its ApiTestCase to write the functional tests, and the problem was that my modifications weren't being applied in the container. But, if I disable the kernel reboot (static::createClient()->disableReboot()), it keeps the modification through all the request at that particular test.

So, I used the code from the tests that @rrajkomar wrote for #221 and disabling the reboot:

static::createClient()->disableReboot();

$cli = static::createClient()->getContainer()->get('eight_points_guzzle.client.<client_name>');
$handlerStack = $cli->getConfig('handler');
$handlerStackRefl = new \ReflectionClass($handlerStack);
$handler = $handlerStackRefl->getProperty('handler');
$handler->setAccessible(true);
$mock = $handler->getValue($handlerStack);

After that, you can append any request into the MockHandler, like:

$mock->append(new Response(200, [], ''));

Api plataform uses SF4, so I just set the MockHandler in the services_test.yaml:

# config/services_test.yaml
eight_points_guzzle:
    clients:
        <client_name>:
            handler: 'GuzzleHttp\Handler\MockHandler'

All that set, you're good to go. I hope it helps ^^

@rrajkomar
Copy link
Contributor

Coming back to this topic after a while, and a lot more use case experience, I'm no longer convinced the MockHandler was been the best way to implement the mocking mechanism.
Now using a lot of Behat tests, I'm faced with an issue that could not be solved by any of the proposed solution (I even tried an adapted version of @matheusfaustino 's solution, sadly it didn't work as expected)

When using Behat testing, the MockHandler that you fill up with MockResponses is not taken into account once the actual code is called.
This is where disabling the client reboot might have solved some use cases when using the symfony driver (or so I hoped) but even then, when switching to any other driver the solution would no longer be working.

All this leads me to believe going towards a middleware based mocking mechanism might be the best way to handle this problem (although I'm definitely not a fan of a record / replay system, this is so far the most usable I found)

Also it might be the opportunity to revisit the discussion we had way back about setting up a base interface for middlewares (#232):
With autowiring now being a best practice, we could use the interface to get an easily extendable system for middlewares.
(The same kind of system could also be used for plugins, currently having to go change the Kernel class is rather cumbersome)

@gregurco WDYT ?

@doppiogancio
Copy link

I am just a couple of years late, I solved these problems building a Guzzle client that uses a router to dispatch mocked routes/responses.

https://packagist.org/packages/doppiogancio/mocked-client

@rrajkomar let me know if my package can help you.

@SherinBloemendaal
Copy link

SherinBloemendaal commented Jan 19, 2024

The solution of @matheusfaustino worked flawless, thnx. As of today, the getConfig() option is deprecated and will be removed in Guzzle 8.0 (guzzle/guzzle#2514). In case your static analyser complains about deprecated function usage this should be the alternative:

$handler = self::getContainer()->get('eight_points_guzzle.handler.{client_name}');
$handler->append(new Response(...));

Btw 1: Removed reflection usage because a service definition is exposed when a handler is set inside the config.
Btw 2: I've also removed the disableReboot() statement, as it's unnecessary when the test only contains a single request. The kernel only reboots during multiple requests. Remember to call disableReboot() if you use self::$client->request() multiple times.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants