Correct unit testing Correct unit testing symfony symfony

Correct unit testing


First, I would like to rewrite the method a tiny bit, if I may.

public function addUser(User $user){    if ($this->doesUserExist($user)) {        throw new UserAlreadyExistException("The user ".$user->getUid()." already exists.");    }    // ... shortened for brevity    $pbnlAccount = $this->userToEntities($user);    $this->ldapEntityManager->persist($pbnlAccount);}

The other relevant method is:

private function doesUserExist(User $user){    $users = $this->ldapRepository->findByUid($user->getUid());    return count($users) === 1;}

Immediately we can see that we basically have two tests:

  • We test that the method throws when the user exists
  • We test that the method persists a PbnlAccount if the user does not exist.

If you do not see why we have these two tests, note that there are 2 possible "flows" in this method: one where the block inside the if statement is executed, and one where it is not executed.

Lets tackle the first one:

public function testAddUserThrowsWhenUserExistsAlready(){    $user = new User();    $user->setUid('123');    $ldapRepositoryMock = $this->createMock(LdapRepository::class);    $ldapRepositoryMock        ->method('findByUid')        ->expects($this->once())        ->with('123')        ->willReturn(new PbnlAccount());    $userRepository = new UserRepository($ldapRepositoryMock);    $this->expectException(UserAlreadyExistException::class);    $userRepository->addUser($user);    }

The second test is left as an exercise for the reader :)

Yes you will have to do some mocking in your case. You wil need to mock the LdapRepository and LdapEntityManager both in this case.

Note 1: this code probably is not runnable, since I do not know the exact details of your code base (and I wrote this off the top of my head), but that is beside the point. The point is that you want to test for the exception.

Note 2: I would rename your function to createNewPbnlAccountForUser(User $user) which is longer, but more descriptive of what it actually does.

Note 3:I am not sure why you are returning $this->getUserByUid() since that seems redundant (you already have the User right there), so I am ommitting that case.


You need to mock ldapEntityManager and all repository services but not the internal function.And as you said, don't boot kernel in unit test. So, you should test all cases with success and throwing exception (make sure to check all behaviour)


If you want to perform a unit test, you should mock all collaborators.Now, entity managers, ldap services and so on should not be mocked (read more here).

Moreover, if you happen to be in a situation where the Arrange part (set mocks, stubs, and so on) is painful and take "a lot" of the test, maybe this is a smell that your class has too many responsibility (is doing too much things).

That said, when I do unit test, I would like the test to fail only for an internal (to the class) reason, not because I've changed a collaborator line that messes up all my tests.