Mocks - uma breve introdução
Mocks são objetos simulados que imitam o comportamento de objetos reais num ambiente controlado de teste.
São utilizados para testar partes do software em isolamento, através da substituição de componentes reais (com que o sistema em teste interage, como serviços web, bases de dados, ou APIs externas) por outros componentes simulados.
Os mocks permitem assim simular diferentes cenários e condições, sem a necessidade de envolver e utilizar os componentes reais, facilitando assim a identificação de problemas e o controlo das variáveis no ambiente de teste.
A necessidade de usar mocks em testes de software surgiu devido a vários desafios enfrentados durante o desenvolvimento e teste de aplicações complexas.
Histórico e Contexto do Uso de Mocks
1. Complexidade Crescente dos Sistemas
Sistemas distribuídos: Com o advento de sistemas distribuídos e arquiteturas baseadas em serviços (como o SOA e, mais recentemente os Microserviços), ad aplicações começaram a ser dependentes de várias partes externas, como APIs, bases de dados, e serviços de terceiros. Testar uma unidade de código isolada tornou-se difícil sem simular esses componentes externos.
Interdependências: À medida que os sistemas se tornaram mais complexos, aumentaram as interdependências entre diferentes módulos e componentes. Ou seja, garantir que todos os componentes estavam disponíveis e funcionais durante os testes tornou-se um grande desafio.
2. Necessidade de Testes Automatizados
Automação de testes: A necessidade automatizar a fim de dar suporte a práticas de desenvolvimento ágeis e DevOps cresceu. Os mocks permitem que os testes automatizados sejam executados de forma rápida e consistente, sem a necessidade de configurar todo o ambiente (como se fosse na vida real).
Reprodução: Para garantir que os testes era reproduziveis e consistentes, era necessário um ambiente de testes controlado. Os mocks ajudaram a garantir que as respostas das dependências externas eram sempre as mesmas durante os testes.
3. Confiabilidade e Robustez
Defeitos isolados: os mocks permitem isolar e testar unidades específicas de código, permitindo assim identificar defeitos mais rapidamente, que é crucial para assegurar que uma falha numa dependência externa não disfarça/esconde defeitos e problemas na componente de software em teste.
Simulação de cenários de erro: Testar cenários de erro (como falhas de rede, respostas de erro de APIs, etc.) é fundamental para garantir a robustez do software. Os mocks facilitam a simulação desses cenários de uma forma controlada.
Evolução do Uso de Mocks
Início do desenvolvimento de software
Nos primórdios do desenvolvimento de software, os testes eram executados única e exclusivamente de forma manual, e por isso era inexistente a necessidade de mocks. Mas, à medida que as aplicações se tornaram mais complexas e a automação de testes se tornou um elemento importante no processo de testes, a necessidade de técnicas para isolar e simular dependências cresceu.
O aparecimento das primeiras ferramentas de mocking
Com a evolução das linguagens orientadas a objetos e o desenvolvimento de práticas de Test Driven Development (TDD), começaram a surgir as primeiras ferramentas e frameworks de mocking, como:
JMock (Java): um dos primeiros frameworks de mocking para Java, que permite criar objetos simulados para testes unitários.
Mockito (Java): mais desenvolvido que o JMock, tornou-se muito popular pela facilidade de utilização e pelas suas funcionalidades.
unittest.mock (Python): Introduzido no Python 3.3, oferece funcionalidades de mocking na sua biblioteca padrão.
Crescimento da popularidade com o desenvolvimento ágil e o DevOps
À medida que as metodologias ágeis e práticas de DevOps se foram tornando populares e mais utilizadas, a automação de testes tornou-se um componente importante (e quase essencial) para garantir ciclos de desenvolvimento rápidos e com qualidade. Os mocks são uma ferramenta importante nesse contexto, ao permitirem:
Integração Contínua (CI): a execução rápida de testes automatizados em cada commit ou pull request.
Entrega Contínua (CD): a garantia de que o código testado pode ser implantado com confiança em ambientes de produção.
Melhores Práticas no Uso de Mocks
Usar Mocks com Moderação: evitar over-mocking, que pode resultar em testes que não reflitam a realidade. Os mocks devem ser utilizados principalmente para isolar unidades de código e simular dependências externas.
Manter a veracidade: certifica-te de que os mocks representam de forma precisa o comportamento das dependências reais, incluindo possíveis erros e exceções.
Isolamento apropriado: utiliza mocks para isolar a unidade de teste, mas evita isolar em demasia em testes de integração, nos quais é importante verificar a interação real entre componentes.
Atualização contínua: Mantém os mocks atualizados sempre que as interfaces das dependências reais mudarem, de forma a garantir que os testes permanecem relevantes e precisos.
Automação de testes: integra os mocks em pipelines de CI/CD para garantir que os testes automatizados são executados rapidamente e de forma consistente.
Exemplos de Ferramentas de Mocking
Mockito (Java): amplamente utilizado para criar mocks e spies para testes unitários.
Sinon.js (JavaScript): popular para mocks, stubs e spies em testes JavaScript, principalmente com frameworks como o Mocha e o Jasmine.
unittest.mock (Python): parte da biblioteca padrão do Python, que oferece uma ampla gama de funcionalidades para criar mocks.
WireMock (Java): utilizado para simular serviços HTTP, permite testes de integração e end-to-end com APIs externas simuladas.