BT

Disseminando conhecimento e inovação em desenvolvimento de software corporativo.

Contribuir

Tópicos

Escolha a região

Início Artigos Testando microservices: 12 técnicas úteis - Parte 1

Testando microservices: 12 técnicas úteis - Parte 1

Pontos Principais

  • Uma arquitetura de microservices depende mais de dependências remotas e menos de componentes em processo, por isto, a estratégia e ambientes de testes precisam se adaptar a estas alterações;
  • Ao testar monolitos usando técnicas pré-existentes, como a virtualização de serviço, não é necessário testar tudo de uma vez, podemos dividir o trabalho e testar os módulos individualmente ou em grupos de componentes que sejam coerentes;
  • Ao trabalhar com microservices, existem várias outras opções disponíveis, porque são implantados normalmente em ambientes que usam containers como o Docker;
  • Precisaremos gerenciar os componentes interdependentes para testar os microservices de maneira econômica. Podemos usar testes duplicados nos testes de microservices que precisam de dependências reais;
  • Dependendo das necessidades, podemos escolher uma das opções listadas neste artigo ou uma combinação delas.

A combinação do estilo arquitetural do microservice e a infraestrutura baseada em container exige uma estratégia de teste compatível com este admirável mundo novo. Uma arquitetura de microservice depende mais de dependências remotas e menos de componentes sendo processados, por isso a estratégia e ambientes de testes precisam se adaptar a essas mudanças.

Mais comunicações entre estruturas resultam em maior esforço focado nos testes de conexões e dos contratos entre os microservices. Além disso, diversas técnicas novas estão disponíveis para lidar com componentes dependentes ao mudar para uma infraestrutura baseada em container, o que geralmente ocorre quando adotamos microservices.

Podemos escolher as técnicas de testes com uma perspectiva no tempo de mercado, custo e risco.

Ao testar os monolitos com técnicas como a virtualização de serviço, não precisamos testar tudo de uma vez. Podemos dividir e testar os módulos individualmente ou em grupos de componentes que sejam coerentes. Criamos ambientes seguros e isolados para os desenvolvedores testarem o trabalho. O uso da virtualização de serviço ao testar monolitos permite dissociar os ambientes de testes de componentes dependentes e reduzir o impacto de problemas como:

  • Dificuldade em provisionar ou configurar componentes dependentes;
  • Dados de testes que levam muito tempo para serem configurados;
  • Bloqueio das equipes por outras que não entregam as APIs no prazo e;
  • Tempo de programação em ambientes de testes.

Ao trabalhar com microservices, temos mais opções porque são implantados normalmente em ambientes que usam containers como o Docker. Nas arquiteturas de microservice, é provável que as equipes usem uma variedade maior de técnicas de testes. Além disso, como os microservices se comunicam pela rede, precisamos testar o impacto das conexões com mais detalhes. O uso de ferramentas e técnicas que melhor se ajustam à nova arquitetura pode permitir um tempo de comercialização menor, menos custos e menos riscos.

Muitos departamentos de TI trabalham com, ou mantêm, sistemas desenvolvidos e implantados em uma arquitetura monolítica típica que possui algumas características, como:

  • As pessoas que trabalham na aplicação são organizadas em equipes separadas dos especialistas: desenvolvedores de UI, de middleware, de backend, administradores de banco de dados e de sistemas;
  • A governança é conduzida centralizada nos arquitetos. Há um conjunto global de regras para qualidade de código, diretrizes de segurança e abordagem dos testes;
  • Os dados são gerenciados de maneira centralizada, de modo que uma aplicação monolítica normalmente depende de um único banco de dados;
  • O nível de automação pode ser baixo, com alguns testes automatizados, mas pouca automação da infraestrutura.

A organização das pessoas que trabalham na aplicação geralmente influencia a forma como os ambientes de desenvolvimento e testes são organizados. Esse efeito é conhecido como lei de Conway. Normalmente, o código será dividido em várias camadas de componentes, como interface do usuário, serviços e repositórios. Os monolitos serão implantados em ambientes compartilhados, geralmente de desenvolvimento, controle de qualidade e testes de aceitação do usuário (UAT). Veja a figura 1.

Muitos sistemas monolíticos foram construídos por equipes que trabalham em silos funcionais, onde a equipe de operações é uma entidade separada que trabalha com um cronograma separado. As mudanças necessárias para introduzir uma abordagem de trabalho em containers em uma empresa como essa podem ser demoradas, pois incluem o provisionamento de nova infraestrutura, além de treinamento da equipe e criação de planos de migração para a mudança de abordagem.

Isso significa que as técnicas de desacoplamento de dependências em arquiteturas monolíticas, geralmente são restritas àquelas que não exigem containers, mas executadas em processo ou em VMs ou hardware provisionados pela equipe de operações. As técnicas que não requerem conteinerização são:

Por causa da Lei de Conway, equipes funcionais isoladas com padrões complexos de comunicação criam monolitos com complexidades semelhantes de comunicação. Isto significa que as ferramentas usadas para a virtualização de serviços precisam ser bastante poderosas, suportando fluxos de trabalho complexos e muitas tecnologias (como diversos protocolos de comunicação) devido à complexidade do sistema de testes (a aplicação monolítica) e à complexidade dos casos de testes.

Normalmente, haverá uma equipe separada responsável pela criação de stubs, mocks ou virtualização de serviço backend ou de terceiros. Isso geralmente resulta em brigas com a equipe responsável pela virtualização, levando a um longo tempo de comercialização e altos custos de manutenção da infraestrutura de testes.

Em uma arquitetura de microservices, normalmente encontramos:

  • Equipes organizadas com base nos recursos comerciais, como equipes multifuncionais de vários desenvolvedores de interface de usuário, middleware e backend, um administrador de banco de dados e um especialista em DevOps;
  • Governança descentralizada que permite que cada equipe escolha a ferramenta certa para o trabalho;
  • Gerenciamento de dados descentralizado que permite que cada microservice, ou grupo de microservices, gerencie os próprios dados;
  • Testes, implantação e infraestrutura são automatizados com pouca ou nenhuma intervenção manual.

Isso influencia quais técnicas estão disponíveis para dissociar um microservice das dependências para fins de testes. Como há menos necessidade de conhecimento tecnológico homogêneo que atenda às necessidades de todas as equipes, cada uma delas geralmente terá acesso a mais opções que atendem às suas necessidades específicas.

As principais categorias de soluções para testar os microservices são aquelas que já estão disponíveis em arquiteturas monolíticas, que também são aplicáveis a esta arquitetura, e aquelas projetadas especificamente para ela.

Os métodos disponíveis nas arquiteturas monolíticas estão usando testes duplicados, como stubs, mocks ou serviços virtuais, conexões a instâncias reais de testes de sistemas de backend ou de terceiros e testes de contrato.

Os métodos disponíveis para arquiteturas de microservices são testes de containers, como testes de containers de banco de dados, testes de containers de virtualização de serviço e testes de containers de serviços terceiros (por exemplo, um teste de container Redis, um teste de container ESB ou um teste de container de dispositivo virtual) e os legados em sandbox.

Existem muitos artigos escritos e palestras sobre como desenvolver uma estratégia para testar os microservices. Aqui estão alguns recursos que consideramos quando temos que fazer esta tarefa:

Usaremos várias das técnicas de testes usadas para arquiteturas monolíticas, bem como novas técnicas que envolvem os containers. No entanto, a adequação de diferentes técnicas de testes podem mudar, uma vez que os loops de feedback nas arquiteturas de microservices são mais rigorosos, pois as equipes geralmente são colocadas em funções cruzadas.

Um tema comum nos recursos listados anteriormente é que precisamos gerenciar os componentes dependentes para testar os microservices de uma maneira rápida e econômica. Dependendo das necessidades, podemos escolher uma das opções listadas neste artigo ou uma combinação delas. Vamos discutir essas opções.

Usando dependências nos testes

Um ambiente de testes para microservices pode usar dependências reais em vez de testes duplicados.

Existem alguns tipos de componentes que o microservice pode se comunicar em um cenário de testes:

  1. Podemos testar o microservice com uma instância de teste de outro microservice. Por exemplo, durante o teste do microservice A, o conectamos a uma instância de teste do microservice B e os testamos juntos;
  2. Podemos testar o microservice com uma instância de produção de outro microservice. Por exemplo, durante o teste do microservice A, o conectamos a uma instância de produção do microservice B e os testamos juntos antes de liberar o microservice A para o deploy;
  3. Podemos testar um microservice com dependências de terceiros. Por exemplo, durante o teste do microservice A, o conectamos a uma instância de produção de um sistema de terceiros;
  4. Podemos testar um microservice com dependências internas não pertencentes a ele. Por exemplo, durante o teste do microservice A, o conectamos a uma instância de teste de um sistema antigo de mainframe em execução no local;
  5. Podemos testar um microservice com dependências que não são de software (hardware). Por exemplo, durante o teste do microservice A, o conectamos a um dispositivo de hardware responsável pela prestação do serviço;

A seguir, vamos listar os componentes dependentes de testes específicos que podemos usar nos testes de microservice, em vez dos listados anteriormente.

Obviamente, podemos usar os chamados testes duplicados nos testes de microservice que fingem ser dependências reais para a finalidade do teste. Temos várias técnicas para escolher, dependendo do tipo de dependência e do problema em questão:

  1. Um mock (no processo ou local/remoto) substitui um objeto do qual o microservice depende por um objeto específico de teste que verifica se o microservice está usando-o corretamente;
  2. Um stub (no processo ou local/remoto) substitui um objeto do qual o microservice depende por um objeto específico de teste que fornece dados de teste ao microservice. Os dados de teste podem ser estáticos ou dinâmicos;
  3. Um simulador (no processo ou local/remoto) é uma versão inteligente do esboço que imita alguns dos comportamentos do sistema do qual o microservice é dependente. Por exemplo, em vez de conectar-se a um sistema de pagamento real, podemos conectar-se a um simulador que implementa parte da funcionalidade que pode ser observável do pagamento;
  4. A virtualização do serviço (local/remoto) também é chamada de simulação de AP. É a prática da substituição de componentes dependentes reais por versões de teste criadas usando ferramentas poderosas de virtualização de serviço. As ferramentas de virtualização permitem uma experiência semelhante ao simulador, mas com menos esforço dos desenvolvedores e testadores. Em vez de criar um teste personalizado duplicado por dependência, as ferramentas disponíveis no mercado cuidam da funcionalidade padrão comum em implementações típicas escritas a mão. As ferramentas de virtualização de serviço geralmente oferecem mais recursos do que stubs ou mocks, como solicitações e respostas de gravação ou suporte interno para várias tecnologias, como HTTP, JMS, FTP ou gRPC;
  5. Podemos usar um banco de dados na memória para substituir uma instância real de um banco de dados somente para os testes;
  6. Podemos executar um container de testes, uma instância de teste de uma dependência por compilação ou pipeline dentro de um container, em vez de usar uma instância compartilhada entre várias equipes, várias compilações ou vários pipelines. Por exemplo, podemos usar um container de testes de banco de dados ou um container de testes de virtualização de serviço;
  7. Podemos usar um legado em sandbox. Em vez de confiar em um ambiente de testes compartilhado, podemos executar um sistema legado em um container. Podemos configurá-lo da maneira apropriada para as necessidades dos testes.

Teste de contrato

O teste de contrato é uma peça crítica do quebra-cabeça ao usar componentes pouco acoplados, como microservices.

Um contrato descreve como os componentes se comunicam e interagem entre si, tanto os formatos de mensagens entre os componentes (sintaxe) quanto às expectativas comportamentais dos componentes (semântica). Usamos o teste de contrato para verificar se os contratos e os componentes são cumpridos. Isso cria confiança de que os componentes podem trabalhar juntos. Ao usar componentes com dependências específicas para testes (como testes duplicados), também é possível usar os testes de contrato para garantir que respeitem a versão mais recente ou específica do contrato. Aqui estão algumas maneiras de testar ou gerenciar contratos entre os componentes:

  1. Em um teste de contrato, os testes representam o snapshot de um contrato entre componentes em um determinado momento. Esse momento pode ficar desatualizado. Podemos testar os momentos dos contratos de maneira automatizada;
  2. As atualizações de snapshots de contrato permitem registrar novamente os contratos entre os componentes. Normalmente, uma atualização atende à sintaxe e parcialmente à semântica do contrato. Podemos consultar os testes de contrato orientado ao consumidor para obter mais detalhes de sintaxe e semântica;
  3. Os testes de contrato orientado ao consumidor é um componente de uma estratégia completa de testes de microservices. Os contratos orientados ao consumidor são separados em produtor e consumidor. Os testes de contrato orientado ao consumidor verificam se o produtor fornece um contrato que atende a todas as expectativas dos consumidores. Os consumidores verificam se os produtores ainda fornecem a estrutura das mensagens e o comportamento de que precisam;
  4. Os testes de integração restrita por contrato podem testar o contrato entre o módulo conector no microservices e o componente dependente. O contrato, nesse caso, é tipicamente mais voltado para o produtor do que para o consumidor;
  5. Podemos usar os testes de contrato para o release de componentes independentes, se desejamos liberar independentemente dois componentes dependentes. Devemos nos lembrar de testar combinações dos artefatos mais recentes e de produção;
  6. Testes de ponta a ponta (E2E) consiste em verificar se todos os componentes funcionam bem juntos para jornadas completas do usuário. Isso significa que os contratos entre os componentes são implicitamente validados ao exercer os testes de jornada do usuário em todo o sistema.

Resumo

Existem muitas técnicas para gerenciar componentes dependentes ao testar os microservices. As informações fornecidas aqui devem preencher algumas lacunas e ajudá-lo a definir a estratégia de desenvolvimento e teste (incluindo o teste de pirâmide).

Na parte 2, compararemos as técnicas com base na maturidade de uma equipe, ritmo de mudança, tempo de mercado, custos e riscos.

Se encontrar algo que não esteja claro no artigo ou se tiver alguma dúvida ou preocupação específica do projeto, entre em contato com os autores: Wojciech Bulaty em wojtek@trafficparrot.com e Liam Williams em liam@trafficparrot.com.

Sobre os autores

Wojciech Bulaty é especializado em arquitetura de teste e desenvolvimento Agile de software, trazendo mais de uma década de experiência prática em codificação e liderança na escrita sobre agilidade, automação, XP, TDD, BDD, programação de pares e codificação limpa. O trabalho mais recente foi feito na Traffic Parrot, onde ajuda as equipes que trabalham com microservices a acelerar a entrega, melhorar a qualidade e reduzir o tempo de deploy, fornecendo uma ferramenta para simulação de API e virtualização de serviços.

Liam Williams é um especialista em automação com foco na melhoria de processos manuais propensos a erros com soluções de software de alta qualidade. É colaborador de projetos open source e autor de várias pequenas bibliotecas. Recentemente, se juntou à equipe da Traffic Parrot, onde voltou a atenção para o problema de melhorar a experiência de teste ao migrar para uma arquitetura moderna de microservice.

Avalie esse artigo

Relevância
Estilo/Redação

Conteúdo educacional

BT