How to login a user in unit test within Symfony in an example

Youssef Benhssaien
4 min readJul 14, 2020

As explained in the Symfony testing, a unit test is a test against a single PHP class, all dependencies should not be tested within this class, so people use mock technic as much as tested services need dependencies, one of common problems is when a service depends on the connected user to do some stuff (check roles, inject user in other entity, …), the Symfony\Component\Security\Core\Security service holds the connected user inside a Token (retrieved by calling the token_storage service), so how to inject a user object inside a token so services can retrieve it ?

Example : In the following example, I will illustrate a Post created by the connected User

The code is available on git :https://github.com/ybenhssaien/sf-unittest-login

  1. Let’s create the Post entity associated with theUser as its author (the connected User )
Post Entity

2. To ensure every created post has an author, we can either create a service with the create method or implement a listener to the doctrine event (PrePersist) in order to inject the connected user as author (I choosed for this example a simple PostService)

PostService

$post->setAuthor($this->security->getUser()) : Injects the connected user as the post’s author.

3. Create a simple unit test against create method to check if the post gets inserted in the database (How to test interacting with the database) and has an author :

PostServiceTest

Note that this test fails as PostService::create() try to inject null in Post entity since User doesn’t exist.

Because of the optional type hint of the parameter Post:setAuthor(?User $user) a Doctrine exception is thrown, forcing the parameter to not accept null, will throw an Error Fatal

So how we can inject a user in the token storage to let PostService retrieve it ?

Thanks to Symfony service decorator we can decorate the token storage 😍

4. We need now to make it possible to login a user with a given role before to launch the test, let’s make a login and logout methods call :

5. The login and logout methods are defined in SecurityTrait :

Note that the property protected static ?User $user is used only to store the last retrieved user from database to minimise queries (There are many ways to do it, I chose the simple one for this example)

6. To make it possible to inject a user in token storage, I’ll create(as mentioned above) a TokenStorageDecorator and make it available as a service (thanks to autowiring)

In case the token is null, create a new one :

7. The last step, to make it work, we need to replace the default token storage by the decorated token storage only in atest environement, I’ll create the config/service_test.yaml file :

With decorates key under the service name, every call for service security.token_storage returns the TokenStorageDecorator instead automatically.

That’s all 😊 tests work now as expected

--

--