Write unit tests for the interface or for the implementation?

Asked

Viewed 766 times

9

Given the following test scenario, where I have the interface:

public interface UserService {

    /**
     * Registra um usuário no sistema.
     * 
     * @param user
     *            Usuário à ser registrado. Não deve ser {@code null}
     * @return Inteiro maior que zero representando o id do usuário registrado,
     *         ou 0 caso o registro falhe
     * @throws IllegalArgumentException
     *             Caso o usuário seja {@code null}
     */
    long register(User user);
}

And a possible implementation that meets the interface javadoc contract:

public class UserServiceImpl implements UserService {
    private UserDao userDao = // Inicializa ou injeta o DAO

    @Override
    public long register(User user) {
        if (user == null) {
            throw new IllegalArgumentException("Usuário não pode ser null");
        }

        try {
            userDao.insert(user);
            long insertedId = user.getId();
            return insertedId;
        } catch (SomeException e) {
            logger.error("Erro ao registrar o usuário " + user.getUsername(), e);
            return 0;
        }
    }
}

The moment I write my unit tests for the method register(User user), should test the implementation UserServiceImpl directly? For example:

public class UserServiceImplTest {
    private UserService service;

    @Before
    public void setUp() {
        service = new UserServiceImpl();
    }

    @Test
    public void shouldReturnIdGreaterThanZero() {
        User user = // Inicializa um usuário pronto para inserção
        long insertedId = service.register(user);

        Assert.assertTrue(insertedId > 0);
    }
}

Or test only the interface, injecting the implementation through a Servicelocator, for example?

public class UserServiceTest { // Note que até mudei o nome da classe de testes
    private UserService service;

    @Before
    public void setUp() {
        service = // Obtém a implementação através de algo parecido com um ServiceLocator
    }

    @Test
    public void shouldReturnIdGreaterThanZero() {
        User user = // Inicializa um usuário pronto para inserção
        long insertedId = service.register(user);

        Assert.assertTrue(insertedId > 0);
    }
}

Which approach should I adopt? It is worth mentioning that new implementations may arise, but I only refer to the interface UserService in my domain code.

Some snippets of code and javadoc have been omitted to simplify code.

  • 2

    Option 1 ;) And by your example, the ideal would be to mock the Userdao, so you do not directly access the bank...

  • 5

    Test the implementation. It makes no sense to test abstraction because otherwise you would have a pluggable test, able to test one implementation or another, so if you have more than one implementation you will have one of them without a test or have two tests pluggable but each linked to a single implementation, making it useless to plug it. Also, testing abstraction the way you suggested the test is dependent on the "service Locator" that will have its own dependencies and the more dependencies worse, as they are more opportunities to fail the test of a code that is working.

1 answer

1


With respect to what to test, you can instantiate the object with the interface type, and by manipulating it use polymorphism to call the implementation and its respective method. This way all objects will be of the type UserService but the methods it will deal with will be the UserServiceImpl. But if you are working with dependency injection and/or inversion control very likely you will have a lot of problem to access the base via test, the ideal is to use mocks or simulate the insertion.

  • 1

    I believe that your response together with the comments of Icaro Bombonato and Caffé answered my question. I am implementing in the proposed form. Thank you all!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.