Proper unit testing

I started using unit testing and functionality with this project, and because of this, I have some questions:

Im working with symfony php map. And I have such a doctrine as an LDAP ORM service.

In addition, I have a custom repository (as a service) that depends on the LDAP ORM service, the registrar, and the validation service.

Now I want to write unit test for the addUser function for UserRepo. It will internally call: getNewUidNumber, userToEntities, doesUserExist and getUserByUid.

My question is: Do I have to make fun of all this inner function just to test addUser function? It would be against the idea of ​​unit test (just check the API).

Or should I just mock the ORM LDAP service, Logger and validation service so that the class calls all the internal functions? But this will cause a huge test function with a lot of taunts, because I have to mock repositories for all calls to internal repositories.

Or I need to start the symfony kernel and use the ServiceContainer to use the LDAP ORM service with a real test database. But wouldn't this be a functional test, not a unit test? I heard that it is bad to have so many dependencies in the test. So I thought it would be bad to use the whole service. Container.

AddUser:

public function addUser(User $user) { $pbnlAccount = $this->userToEntities($user); if(!$this->doesUserExist($user)) { $pbnlAccount->setUidNumber($this->getNewUidNumber()); $this->ldapEntityManager->persist($pbnlAccount); $this->ldapEntityManager->flush(); } else { throw new UserAlreadyExistException("The user ".$user->getUid()." already exists."); } return $this->getUserByUid($user->getUid()); } 

For more code, such as internal functions: https://gist.github.com/NKPmedia/4a6ee55b6bb96e8af409debd98950678

Thanks Paul

+5
source share
3 answers

First, I would like to rewrite the method as a small bit, if possible.

 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); } 

Another relevant method:

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

It’s immediately obvious that we basically have two tests:

  • We verify that the method generates when the user exists.
  • We verify that the method saves PbnlAccount if the user does not exist.

If you don’t see why we have these two tests, note that in this method there are two possible “threads”: one where the block is executed inside the if statement, and one where it is not executed.

Let's look at the first option:

 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 mockery of your case. You need to make fun of LdapRepository and LdapEntityManager as in this case.

Note 1: this code probably does not work, since I do not know the exact data about your code base (and I wrote it from my head), but this is not relevant. The point is you want to test 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'm not sure why you are returning $this->getUserByUid() , as this seems redundant (you already have a User), so I am sealing this case.

+2
source

You need mock ldapEntityManager and all repository services, but not an internal function. And, as you said, do not load the kernel into unit test. So, you should check all success cases and throwing exceptions (be sure to check all behavior)

+1
source

If you want to perform a unit test, you must mock all employees. Now entity managers, ldap services, etc. You should not taunt ( read further here ).

In addition, if you find yourself in a situation where the Arrange part (set mocks, stubs, etc.) is painful and takes a lot of test, it may be a smell that your class has too much responsibility (does too many things).

However, when I do the unit test, I would like the test to fail only for the internal reason (for the class), and not because I changed the co-author line, which ruined all my tests.

0
source

Source: https://habr.com/ru/post/1271700/


All Articles