Teste de ponta a ponta de microsserviços com o Catcher

Boa tarde Gostaria de apresentar uma nova ferramenta para testes de ponta a ponta de microsserviços - Catcher
logo


Por que testar?


Por que preciso do teste e2e? Martin Fowler recomenda evitá-lo em favor de testes mais simples.


No entanto, quanto mais altos os testes, menor é a reescrita. Os testes de unidade são quase completamente reescritos. Os testes funcionais também precisam gastar seu tempo em caso de refatoração séria. Os testes de ponta a ponta devem testar a lógica de negócios e mudam com menos frequência.


Além disso, mesmo uma cobertura de teste completa de todos os microsserviços não garante a interação correta. Os desenvolvedores podem implementar incorretamente o protocolo (erros no nome / tipo de dados).


Ou implemente uma nova funcionalidade que depende do esquema de dados da documentação e obtenha uma surpresa no ambiente do produto na forma de erros de incompatibilidade de esquema: uma bagunça nos dados ou alguém se esqueceu de atualizar o esquema de dados.


E os testes de cada um dos serviços envolvidos serão verdes.


Por que testes automáticos?


Sério. No meu local de trabalho anterior, foi decidido que gastar tempo implantando testes automatizados é muito longo, difícil e caro. O sistema não é grande (10 a 15 microsserviços com um kafka comum). O CTO decidiu que "os testes não são importantes, o principal é que o sistema funcione". Testado manualmente em vários ambientes.


Como ficou (processo geral):


  1. Concorde com outros desenvolvedores (lançando todos os microsserviços que participam da nova funcionalidade)
  2. Distribuir todos os serviços
  3. Conectar ao kafka remoto (ssh duplo em dmz)
  4. Conectar-se aos logs do k8s
  5. Formule manualmente e envie uma mensagem para kafka (graças a deus json)
  6. Observe os logs, tentando entender se funcionou ou não.

E agora há um pouco de alcatrão neste barril de chocolate: a maioria dos testes precisava ser criada pelos usuários, porque a reutilização dos existentes é difícil.


Em primeiro lugar, devido ao fato de o sistema ser distribuído, vários serviços possuíam bancos de dados próprios contendo informações sobre os usuários.


Em segundo lugar, o kafka foi usado para armazenamento permanente de dados. I.e. mesmo se as informações forem excluídas / alteradas no banco de dados, o serviço ainda as lerá novamente após a reinicialização.


Como era o registro de um novo usuário de teste (aproximadamente):


  1. Digite quaisquer dados (nome, email, etc.)
  2. Inserindo dados pessoais (endereço, telefone, qualquer informação tributária)
  3. Inserindo dados bancários (na verdade, dados bancários)
  4. Responda 20-40 perguntas (você já sente dor?)
  5. Passe pela identificação do IDNow (obrigado, eles o desativaram no ambiente de desenvolvimento, obrigado, no palco são cerca de 5 minutos ou mais, porque a caixa de areia às vezes está sobrecarregada)
  6. Nesta etapa, é necessário abrir uma conta em um sistema de terceiros e nada pode ser feito através do front-end. Você precisa ir de ssh para kafka e trabalhar como um servidor falso (envie uma mensagem de que a conta está aberta)
  7. Em seguida, você precisa ir para outro front-end na conta pessoal do moderador e confirmar o usuário.

Super, o usuário está registrado! Agora, mostre a pomada: alguns testes exigem mais de um usuário. E às vezes os testes falham na primeira vez.


E como é a verificação da nova funcionalidade e confirmação da equipe de negócios?
Tudo o mesmo precisa ser repetido no ambiente a seguir.


Escusado será dizer que, depois de um tempo, você começa a se sentir como um macaco, que apenas faz o que pressiona, registrando usuários.


Alguns outros desenvolvedores (geralmente o front end) tiveram problemas para se conectar ao kafka. E com um bug no terminal com mais de 80 caracteres (nem todo mundo sabia sobre o tmux).


Prós :


  • não é necessário configurar / escrever nada. Teste diretamente em um ambiente em execução.
  • não requer qualificações elevadas (especialistas mais baratos podem fazê-lo)

Contras :


  • leva muito tempo (quanto mais longe, mais)
  • geralmente apenas novas funcionalidades são testadas (não está claro se a existente foi quebrada)
  • Freqüentemente, desenvolvedores qualificados estão envolvidos em testes manuais (especialistas caros fazem um trabalho barato).

Como automatizar?


Se você ler isso balançando a cabeça e dizendo: "Sim, é um ótimo processo, os caras sabem o que estão fazendo", então você não ficará interessado.


Os testes caseiros do e2e são de dois tipos e dependem de qual dos programadores foi mais livre:


  • o back-end que vive em seu ambiente de teste. Ele protege a lógica de teste, que se contrai nos pontos finais. Pode até ser parcialmente automatizado devido à interação com o IC.
  • Um script com a mesma lógica com fio. A única diferença é que você precisa ir a algum lugar e executá-lo a partir daí. Se você confia no seu IC, pode executá-lo automaticamente.

Isso parece bom. Problemas?


Sim, esses testes são escritos sobre o que a pessoa que os escreve sabe. Normalmente, essas são linguagens de script como rub ou python, que permitem escrever com rapidez e facilidade esse tipo de coisa. No entanto, às vezes você pode se deparar com vários scripts bash, C ou algo mais exótico (passei uma semana copiando minha bicicleta em scripts bash para python, porque os scripts não eram mais extensíveis e ninguém realmente sabia como eles funcionavam e o que estavam testando) .
Exemplo de projeto aqui


Prós :


  • automação

Contras :


  • requisitos adicionais para as qualificações dos desenvolvedores são possíveis (se eles estão desenvolvendo em Java e os testes foram escritos em Python)
  • escrevendo código para testar código escrito (quem testará os testes?)

Existe alguma coisa pronta?


Claro, basta olhar na direção do BDD . Há um pepino , há um medidor .


Em resumo, o desenvolvedor descreve o cenário de negócios em um idioma especial e, em seguida, implementa as etapas do script no código. A linguagem é geralmente legível por humanos e supõe-se que será lida / gravada não apenas pelos desenvolvedores, mas também pelos gerentes de projeto.


Os scripts, juntamente com a implementação das etapas, também estão em um projeto separado e são executados por produtos de terceiros (Cucumber / Gauge / ...).


O script fica assim:


Customer sign-up ================ * Go to sign up page Customer sign-up ---------------- tags: sign-up, customer * Sign up a new customer with name "John" email "jdoe@test.de" and "password" * Check if the sign up was successful 

E implementação:


 @Step("Sign up as <customer> with email <test@example.com> and <password>") public void signUp(String customer, String email, String password) { WebDriver webDriver = Driver.webDriver; WebElement form = webDriver.findElement(By.id("new_user")); form.findElement(By.name("user[username]")).sendKeys(customer); form.findElement(By.name("user[email]")).sendKeys(email); form.findElement(By.name("user[password]")).sendKeys(password); form.findElement(By.name("user[password_confirmation]")).sendKeys(password); form.findElement(By.name("commit")).click(); } @Step("Check if the sign up was successful") public void checkSignUpSuccessful() { WebDriver webDriver = Driver.webDriver; WebElement message = webDriver.findElements(By.className("message")); assertThat(message.getText(), is("You have been signed up successfully!")); } 

Projeto completo aqui


Prós :


  • a lógica de negócios é descrita em linguagem legível por humanos e armazenada em um único local (pode ser usada como documentação)
  • soluções prontas são usadas, os desenvolvedores precisam apenas saber como usá-las

Contras :


  • gerentes não irão ler e escrever scripts
  • você deve seguir as especificações e sua implementação (e isso é escrever código e editar especificações)

Bem, então por que Catcher?


Claro, para simplificar o processo.


O desenvolvedor grava apenas scripts em json / yaml, e o Catcher os executa. O script consiste em etapas executadas sequencialmente, por exemplo:


 steps: - http: post: url: '127.0.0.1/save_data' body: {key: '1', data: 'foo'} - postgres: request: conf: 'dbname=test user=test host=localhost password=test' query: 'select * from test where id=1' 

O Catcher suporta modelos jinja2, para que você possa usar variáveis ​​em vez de valores com fio no exemplo acima. Variáveis ​​globais podem ser armazenadas em arquivos de inventário (como em um conjunto), extraídas do ambiente e registradas novas:


 variables: bonus: 5000 initial_value: 1000 steps: - http: post: url: '{{ user_service }}/sign_up' body: {username: 'test_user_{{ RANDOM_INT }}', data: 'stub'} register: {user_id: '{{ OUTPUT.uuid }}' - kafka: consume: server: '{{ kafka }}' topic: '{{ new_users_topic }}' where: equals: {the: '{{ MESSAGE.uuid }}', is: '{{ user_id }}'} register: {balance: '{{ OUTPUT.initial_balance }}'} 

Além disso, você pode executar as etapas de teste:


 - check: # check user's initial balance equals: {the: '{{ balance }}', is: '{{ initial_value + bonus }}'} 

Você também pode executar alguns scripts de outros scripts, o que tem um excelente efeito na limpeza e reutilização do código (incluindo iniciar apenas parte das etapas no sistema de tags, atraso no lançamento, etc.).


 include: file: register_user.yaml as: sign_up steps: # .... some steps - run: include: sign_up # .... some steps 

Inserir e usar scripts pode resolver o problema de aguardar um recurso (aguarde um serviço enquanto ele é iniciado).


Além das etapas internas já prontas e de um repositório adicional), é possível escrever seus módulos em python (apenas herdando do ExternalStep ) ou em qualquer outro idioma:


 #!/bin/bash one=$(echo ${1} | jq -r '.add.the') two=$(echo ${1} | jq -r '.add.to') echo $((${one} + ${two})) 

e use:


 --- variables: one: 1 two: 2 steps: - math: add: {the: '{{ one }}', to: '{{ two }}'} register: {sum: '{{ OUTPUT }}'} 

Os scripts são colocados no arquivo de janela de encaixe e executados no CI.


Também esta imagem pode ser usada no Marathon / K8s para testar o ambiente existente. No momento, estou trabalhando em um back-end (semelhante ao AnsibleTower) para tornar o processo de teste ainda mais fácil e conveniente.


Prós :


  • não é necessário escrever código (apenas no caso de módulos personalizados)
  • alternando ambientes por meio de arquivos de inventário (como no conjunto)
  • Você pode usar seus próprios módulos (em qualquer idioma, mesmo sh)

Contras :


  • sintaxe não legível por humanos (em comparação com as ferramentas BDD)

Em vez de uma conclusão


Quando escrevi essa ferramenta, só queria reduzir o tempo que costumo gastar em testes. Aconteceu que toda nova empresa precisa escrever (ou reescrever) esse sistema.


No entanto, a ferramenta acabou sendo mais flexível do que eu esperava. Se alguém estiver interessado no artigo (ou na própria ferramenta), posso lhe dizer como usar o Catcher para organizar migrações centralizadas e atualizar o sistema de microsserviço.


Upd


Como fui indicado nos comentários, o tópico não é divulgado.
Vou tentar indicar aqui as teses mais controversas.


  • testes de ponta a ponta não são testes de unidade. Eu já me referi a M. Fowler neste artigo. Os testes de unidade estão no projeto de back-end de teste (diretório de tests padrão) e são executados toda vez que o código é alterado para CI. E os testes e2e são um projeto separado, geralmente levam mais tempo, testam a interação de todos os serviços participantes e não sabem nada sobre o código do seu projeto (caixa preta).
  • você não deve usar o Catcher para testes de integração (e abaixo). É caro É muito mais rápido escrever um teste no seu idioma para o back-end atual. Você precisará de testes de ponta a ponta apenas se a lógica de negócios estiver espalhada por 2 ou mais serviços.
  • Catcher também é um BDD. Do meu ponto de vista, a principal vantagem sobre o Gauge / Pepino são os módulos prontos e a facilidade de adicioná-los. Idealmente, apenas um teste é escrito. Na última empresa, escrevi todos os 4 testes em componentes padrão, sem programar nada. Consequentemente, os requisitos de qualificação (e o preço de um especialista) serão mais baixos. Somente o conhecimento de json / yaml e a capacidade de ler as especificações são necessários.
  • Para escrever testes do Catcher, você precisará aprender o Catcher-DSL. Infelizmente, é verdade. No começo, eu queria fazer os testes escreverem eles mesmos, diretamente do microfone. Mas então eu pensei que eles iriam me despedir como desnecessário;) Como mencionado acima - Catcher DSL é o json / yaml e as especificações padrão da etapa. Nada fundamentalmente novo.
  • Você pode usar tecnologias padrão e escrever algo de sua preferência. No entanto, estamos falando de microsserviços. Este é um grande número de diferentes tecnologias e armas nucleares e um grande número de equipes. E se o comando java junit + testcontainers é a escolha óbvia, a equipe erlang escolherá outra coisa. Em uma grande empresa com mais de 30 equipes no topo, eles decidirão que todos os testes devem ser submetidos à nova equipe de infraestrutura / qa. Você pode imaginar como eles estão felizes neste zoológico?
  • Se você tiver 4-5 testes e2e, poderá escrever tudo em qualquer linguagem de script e esquecê-lo. No entanto, se a lógica mudar com o tempo, depois de 2 a 4 anos, você precisará refatorar, distribuindo diretamente a lógica comercial dos testes e a implementação de métodos de acesso aos componentes testados. Então, no final, você escreve seu Catcher, apenas não tão flexível. Foram necessárias 4 implementações para entender isso;)

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


All Articles