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

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):
- Concorde com outros desenvolvedores (lançando todos os microsserviços que participam da nova funcionalidade)
- Distribuir todos os serviços
- Conectar ao kafka remoto (ssh duplo em dmz)
- Conectar-se aos logs do k8s
- Formule manualmente e envie uma mensagem para kafka (graças a deus json)
- 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):
- Digite quaisquer dados (nome, email, etc.)
- Inserindo dados pessoais (endereço, telefone, qualquer informação tributária)
- Inserindo dados bancários (na verdade, dados bancários)
- Responda 20-40 perguntas (você já sente dor?)
- 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)
- 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)
- 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 :
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:
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;)