DEV Community

3 motivos do porquê testes unitários não são suficientes para Microservices com Spring Boot

Jordi Henrique Silva on December 14, 2022

Escrever testes automatizados já é rotina para maioria dos times de tecnologia, porém, o que não te contaram é que testar microsserviços Spring Boo...
Collapse
 
scovl profile image
Vitor Lobo

E aí @jordi Henrique Silva, tudo bom? @loboriseup aqui (twitter). No primeiro item, no caso, primeiro motivo, há um pequeno equívoco no argumento. Explico: dá uma olhada neste artigo escrito pelo Baeldung (baeldung.com/injecting-mocks-in-sp...). Nele, é demonstrado como usar a injeção de dependência para inserir mocks Mockito em beans do Spring para testes unitários. O objetivo dessa abordagem é fornecer isolamento adequado aos testes, permitindo que se foque na funcionalidade de uma unidade específica sem envolver toda a hierarquia de classes em cada teste. Exemplo:

@ActiveProfiles("test")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MocksApplication.class)
public class UserServiceUnitTest {
    @Autowired
    private UserService userService;

    @Autowired
    private NameService nameService;

    @Test
    public void whenUserIdIsProvided_thenRetrievedNameIsCorrect() {
        Mockito.when(nameService.getUserName("SomeId")).thenReturn("Mock user name");
        String testName = userService.getUserName("SomeId");
        Assert.assertEquals("Mock user name", testName);
    }
}
Enter fullscreen mode Exit fullscreen mode

Aqui é um exemplo de como é possível utilizar mocks Mockito em beans do Spring para testes unitários, validando comportamentos e configurações relacionados ao contexto do Spring. Através da injeção de mocks, é possível isolar a unidade de código em teste e verificar seu comportamento de forma específica, sem envolver as dependências reais do Spring. Portanto, o argumento de que os testes de unidade com mocks não validam comportamentos e configurações relacionados ao contexto do Spring não é válido, pois é possível alcançar essa validação por meio dessa abordagem de teste unitário com mocks Mockito e injeção de dependência no Spring.

Collapse
 
jordihofc profile image
Jordi Henrique Silva

Obrigado por compartilhar @lobocode, acho que o primeiro ponto que temos que discutir aqui é o que são testes unitários e o que são testes de integração.

Testes de unidade são aqueles que testamos nossas classes de forma isolada, o que significa que é minha classe se comunica apenas com código que eu ou meu time escreveu. Já os testes de integração que me refiro no artigo são aquelas que se integram com dependências externas, como o próprio contexto do Spring.

Acho que também é importante ressaltar que o código demostrado no artigo do baeldung favorece que você injete Mocks no contexto do Spring, o que é feito hoje através de @MockBean. Um detalhe a se notar é que quando você sobe o Contexto do Spring em um ambiente de teste você NÃO está testando de forma unitária, mas sim de forma integrada. E fazendo com que os Beans sejam criados e integrados ao Container de IoC/DI.

Collapse
 
scovl profile image
Vitor Lobo • Edited

Jordi, olha só, sobre o segundo motivo acima, novamente no Baeldung vc encontra este artigo aqui: baeldung.com/spring-test-pyramid-p.... No link é abordado diferentes tipos de testes e a importância de cada um deles. De acordo com esse artigo, existem três tipos principais de testes: testes unitários, testes de integração e testes de interface do usuário (UI). Ou seja, teste unitário e teste de integração tem abordagens e objetivos diferentes Jordi.

Observe que a abordagem do teste de pirâmide, proposta por Mike Cohn, sugere que escrevamos testes com diferentes níveis de granularidade e quantidade, com maior ênfase nos testes unitários e menor quantidade de testes conforme ampliamos o escopo dos testes. A ideia é que a quantidade de testes siga a forma de uma pirâmide, sendo maior nos níveis mais granulares e diminuindo conforme avançamos para testes mais amplos.

Com base no artigo do Baeldung, podemos concluir que testes unitários, testes de integração e testes de UI têm suas próprias finalidades e devem ser combinados de forma adequada para garantir uma cobertura abrangente dos testes em um contexto de microsserviços com Spring Boot. Se quiser trocar uma ideia, me segue lá bolha.us/@lobocode.

Collapse
 
jordihofc profile image
Jordi Henrique Silva • Edited

A pirâmide de testes é uma abordagem de fato muito utilizada e auxilia desenvolvedores e times de tecnologia a traçar estratégias para escrita de uma suíte de testes para o sistema.

O fato que deve ser notado aqui é que no contexto de microsserviços onde temos pequenas bases de código, operações que se concentram em I/O, Magias feitas por baixo dos panos através de IoC/DI os testes de unidade NÃO serão suficientes. E para estes casos a sugestão é FAVORECER os teste de integração. Então antes quando você tinha maior quantidade de testes de unidade, agora você pode redistribuir a proporção, talvez formando um losangolo.

Se quiser saber mais recomendo que leia:
martinfowler.com/articles/2021-tes...

E se quiser continuar discutindo sobre me segue lá no twitter:

@JordiHSilva

Collapse
 
scovl profile image
Vitor Lobo

Que mancada a minha de confundir Fowler com Uncle Bob.

Collapse
 
dearrudam profile image
Maximillian Arruda

Muito massa o artigo!!! Quanto mais utilizamos bibliotecas e frameworks como Bean Validations ou JPA por exemplo, testes de integração são necessários para ter certeza quanto ao comportamento e execução da aplicação...ainda bem que o Spring, Quarkus e outros ferramentas estão cada vez mais nos ajudando com essas tarefas!!! Sim, sei que há casos que não há necessidade de subir um ambiente integrado para testar alguma regra de negócio, mas acho que testes de integração expõe uma perpectiva muito mais próxima de uma situação em produção!!! Parabéns pelo artigo!!!

Collapse
 
jordihofc profile image
Jordi Henrique Silva

Muito obrigado por expor seu ponto de vista Max!
Os testes integrados oferecem feedbacks mais fieis quanto o comportamento do software em cenários de produção.
Ainda mais que hoje, temos pequenas bases de código que na maioria das vezes são focados em fazer I/O.

Collapse
 
viniciusxyz profile image
Vinicius Vieira dos Santos

Concordo com sua visão sobre não ser suficiente, mas eu penso que testes unitários são mais simples de fazer e executar justamente pq envolvem poucos componentes ( afinal é UNitário ) e quando temos um fluxo de testes automatizados conseguimos pegar uma boa quantidade de bugs com eles antes de chegar a etapa do teste integrado por isso creio que devem ser os primeiros a serem escritos, uma boa cobertura de testes unitários provê muiito mais confiança no novo código. O texto descreve bem os problemas que temos quando usamos apenas testes unitários, mas sinto que uma pessoa que tem menos experiência pode acabar lendo ele como "abandonem os testes unitários o negócio agora é teste integrado" o que considero problemático, então valeria um adendo da importância deles, nos últimos anos tenho seguido a política de usar ambas as formas de testar a qualidade do produto e tem dado muito certo, inclusive sinto que onde consegui captar mais erros de forma mais barata foi nos testes unitários.

Collapse
 
jordihofc profile image
Jordi Henrique Silva

Obrigado por expor sua visão Vinicius! E sim, os testes unitários têm seu valor, uma boa suíte deve ser composta por ambas as categorias.

A motivação do artigo é demostrar que no contexto de microsserviços onde a maior proporção do código é composta por validação e operações de I/O, os testes unitários não conseguem identificar se características essenciais do software funcionam como deveria.
Por exemplo, caso uma anotação seja usada equivocadamente, ou se for removida
o teste não irá falhar, ou seja, uma falsa impressão de segurança sera dada.

Dado a isso, o artigo sugere favorecer mais testes integrados do que unitários para este contexto.

Collapse
 
eguadorodrigo profile image
Rodrigo

Primeiro, parabéns pelo artigo!
Provocação: em se tratando do dia-a-dia, como seriam postos a responsabilidade dentro de um time?
Olhando pra tal pirâmide de testes, o de unidade os de integração e os ponta a ponta, cada um além de ter seus próprios objetivos, ao meu ver, também teriam responsáveis diferentes. É importante entender, aplicar, mas acho válido a separação de até onde vai a responsabilidade de implementar cada teste, vide que em uma equipe é desejável ter um time multidisciplinar.
Mas concordo com a premissa de que testes devem ter os três aspectos para alavancar a qualidade da aplicação: cobertura/integração/e2e.
E ainda assim, as aplicações possuem outras métricas, contextos de infra e é responsabilidade de todo time, buscar a excelência.
Enfim, ótimo tópico e abordagem, exemplos e didática!

Collapse
 
jordihofc profile image
Jordi Henrique Silva

@rodrigo obrigado por compartilhar suas provocações!

em se tratando do dia-a-dia, como seriam postos a responsabilidade dentro de um time?

Antes de descrever como vejo a divisão de responsabilidades no dia a dia, acho importante alinhar o que entendo como teste de integração.

Para mim, teste de integração é quando valido a integração do código que escrevi com qualquer outro código, inclusive do framework que estou utilizando. Olhando para isso, acho que tanto os testes de unidade quanto os de integração devem ser escritos e mantidos pelos desenvolvedores.

Olhando para times multi-disciplinares, vejo que os testes de aceitação e ponta a ponta podem ser transferidos para o QA ou QE, que possuem mais técnicas para escrita de testes, inclusive os não funcionais.

Collapse
 
andersonsantana profile image
Anderson Santana

Mano, já passei por projetos onde só era feito testes unitários da use case, achava aquilo bizarro.