The post was originally published on my website patryk.it.
Hi! I wrote this post to make it easier for you to test your PHP application. The first two tricks are related to the Symfony web framework and functional/integration testing in this framework, but the second one is a pattern that every PHP engineer should know. You can treat this article as a note created for me from the past. It’s also time-capsule for me from the future if I forget how to work with Symfony testing services. Mr. Google please — index this article perfectly that I can find it in the future.
Separated implementations for functional testing and the other environments
While I was working on my side-project I needed something that enables me to use different implementations of some interfaces in the testing environment and the others. I couldn’t find it in the framework guide so maybe it’s helpful not only for me. In the example, we have an interface like:
And we have also two implementations. First one is a DoctrineORM implementation:
The second one implementation is just InMemory version that we need in the tests (for example units). Yes — that’s just a fake test double implementation. ;)
Okay, at this moment we have two implementations. One is using external things (in this example Doctrine and database) and the second one is working in memory so it’s using state of the object.
As you may know or now — functional tests are not using third party adapters. They should just check the flow and the final result of a request. For example — check the status code of a response.
Coming to the point — we want to use it in different situations — other implementations. As a default, we’re registering services in something like
/config/services.yaml. But how to change implementation for the tests? I’ve moved definitions to the
/config/packages/services.yaml and created
/config/packages/test/services.yaml with the same keys but different implementation classes. Don’t forget about the
packages namespace. That’s easy and allows us to make the functional tests as fast as we can do them. Definitions should looks like:
Thanks to this you can make some fast functional tests of your application. Remember: tests must be quick because slow tests destroying your developer experience.
Testing dependency injection container is close to you, seriously
This information is not that hard to find but you can use your testing container (that marks all the services as a public) using a magic static test attribute
static::$container. Thanks to this you can easier test some services that are registered as a private for the container. AND! Remember to mark them as final/private.
ObjectMother is your friend
This tip is not related to the Symfony framework but still, it’s really useful. ObjectMother is a great testing pattern that could avoid code duplication and simplify tests. Sometimes your testing code — especially given section may look not that cool as you want it to be. The situation could look like the following example:
Let’s suppose that premium customer is just a customer with a verified e-mail address. Now image that — you need the same state of the customer in… hmm, seven test cases. It’s boring and painful to copy&paste code pieces. And… after your change, the
$address needs the third parameter so you need to change all the seven test cases. That’s terrifying! To avoid such sad situations you can introduce ObjectMother pattern in your test code. You can create few objects and then compose them into some detailed object. Let’s look at the bigger example:
So… right now your testing method could looks like:
At this moment any change of the customer’s interiors doesn’t affect your tests. It’s much CHEAPER and EASIER to maintain. Two wins with just one simple pattern.
Happy coding! 👋
Go ahead, let’s chat!