Os testes de integração adequados exigem uma configuração completa de sua infraestrutura. E isso pode ser um pouco difícil de colocar em prática às vezes, especialmente quando você precisa dar suporte ao seu lapop de desenvolvedor, ao seu nó de CI ou a qualquer outra máquina em que precise executar os testes. Uma boa solução para corrigir isso é usar contêineres como um tempo de execução comum. O Docker funciona da mesma forma em todas as máquinas. Portanto, desde que esteja instalado em seu laptop de desenvolvedor e em seu nó de CI, não há problema :) Então, mais uma vez, mostrarei a você como fazer isso usando Contêineres de teste.
O que queremos testar
Há um ótimo aplicativo de amostra para testar o Couchbase com o Travel Sample. O backend Java está localizado aqui https://github.com/couchbaselabs/try-cb-java/ e o front-end aqui https://github.com/couchbaselabs/try-cb-frontend/. Ele permitirá que você entenda vários recursos, como N1QL, FTS ou subdocumentos. É um ótimo lugar para começar a aprender o Couchbase. E, embora seja um ótimo lugar para aprender, ainda não temos nenhum teste de integração para ele. Portanto, iniciei um novo projeto para remediar isso. Ele está disponível em https://github.com/couchbaselabs/try-cb-integration-test. É sobre isso que escreverei hoje.
Configuração de teste
Para testar o frontend, usarei o Selenium. Ele permite que você especifique basicamente onde deseja clicar em uma página, digitar teclas e verificar o conteúdo de uma página. Assim, você pode dizer ao driver do Selenium para carregar uma página em um determinado URL, verificar se ela foi carregada corretamente, clicar em um link e verificar se a nova página o levou ao local desejado. Isso é tudo o que faremos hoje em termos de testes. O importante aqui é como configurar isso. Primeiro, dê uma olhada no meu código de teste:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
@Teste público vazio testTab() lançamentos InterruptedException { Driver Web remoto motorista = cromo.getWebDriver(); motorista.obter("http://trycbfront"); WebElement usernameField = (novo WebDriverWait(motorista, 10)) .até que(Condições esperadas.presenceOfElementLocated(Por.nome("nome de usuário"))); // INSCREVA-SE usernameField.sendKeys("ld@cb.com"); motorista.findElementByName("senha").sendKeys("senha"); motorista.findElementByTagName("botão").clique(); // Verificar a ASSINATURA Cordas textElement = (novo WebDriverWait(motorista, 10)) .até que(Condições esperadas.presenceOfElementLocated(Por.xpath(("/html/body/app-root/div/div[2]/div[1]/div/div[2]/app-home/div[1]/div/div[1]/div[1]/div/div/div[2]/div/small/strong")))).getText(); Afirmar.assertEquals("Encontre um voo", textElement); // Página do carrinho de compras de teste navigateToCart(motorista); textElement = (novo WebDriverWait(motorista, 10)) .até que(Condições esperadas.presenceOfElementLocated(Por.xpath(("/html/body/app-root/div/div[2]/div[1]/div/div[2]/app-cart/div[1]/button")))).getText(); Afirmar.assertEquals("Limpar carrinho", textElement); } público vazio navigateToCart(Driver Web remoto motorista) { motorista.findElementByXPath("/html/body/app-root/div/div[2]/div[1]/div/div[1]/div/app-navbar/div/div[3]/div/a[2]").clique(); } |
Se estiver familiarizado com o Selenium, seus olhos provavelmente estão doendo ao olhar para esses valores XPath grandes e longos. Na maioria dos aplicativos, isso será substituído pelo id do elemento DOM que você está procurando. Agora que já tiramos isso do caminho, dê uma olhada na primeira linha: RemoteWebDriver driver = chrome.getWebDriver();
O driver do Selenium foi fornecido por um objeto chrome. Esse objeto representa um contêiner que executa o selenium. Isso é possível graças ao TestContainers e sua integração com o Selenium. Mas, como se trata de um contêiner, para acessar outra coisa, ou você a constrói no contêiner ou precisa se vincular a outro contêiner. É claro que foi isso que foi feito. Para executar o Travel Sample, você precisa de três coisas. Você precisa de um servidor Couchbase, um backend de amostra de viagem e um frontend de amostra de viagem, de preferência todos em seu próprio contêiner. Felizmente, temos uma imagem do Docker para cada um deles. Consulte a imagem Leia-me do projeto se você quiser construí-los.
Para que tudo isso funcione, você precisa iniciá-los em uma ordem específica. Primeiro, inicie o Couchabse porque ele é necessário para o backend. Em seguida, inicie o backend porque ele é necessário para o frontend, e depois o frontend porque ele é necessário para a imagem do Selenium que contém o selenium e um navegador. Portanto, precisamos garantir que cada contêiner seja iniciado e esteja pronto antes de iniciar o outro. Digo pronto porque, em alguns casos, pode ser diferente de iniciado. Quando você inicia um contêiner do Couchbase, precisa configurá-lo. Quanto ao aplicativo Spring Boot para o backend, ele leva algum tempo para estar pronto para aceitar conexões. Felizmente, o TestContainers pensou nisso e fornece um mecanismo de espera interessante.
Vamos começar com o contêiner do Couchbase:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
público estático final Cordas usuário do cluster = "Administrador"; público estático final Cordas clusterPassword = "senha"; público estático CouchbaseContainer couchbaseContainer = novo CouchbaseContainer() .comFTS(verdadeiro) .com índice(verdadeiro) .comQuery(verdadeiro) .comTravelSample(verdadeiro) .comClusterUsername(usuário do cluster) .comClusterPassword(clusterPassword) .comNewBucket(DefaultBucketSettings.construtor().enableFlush(verdadeiro).nome("default").cota(100).réplicas(0).tipo(Tipo de balde.BASE DE SOFÁ).construir()); estático { couchbaseContainer.iniciar(); } |
Você pode ver que ele pode ser facilmente configurado com essa API fluente. Aqui, estou configurando-o de modo que ele tenha os serviços FTS, Index e Query ativados, que a amostra de viagem seja pré-carregada, com meu próprio nome de usuário e senha e também com um bucket padrão.
Normalmente, com TestContainers, você teria uma @ClassRule em seus testes. Isso iniciaria o contêiner automaticamente. Mas isso iniciaria tudo com uma @ClassRule ao mesmo tempo. Infelizmente, não podemos fazer isso porque, conforme explicado anteriormente, os contêineres precisam estar prontos antes de iniciar o próximo. Por isso, podemos nos livrar disso adicionando um bloco de código estático. Todos eles são executados em ordem. Portanto, se iniciarmos nosso contêiner em um bloco de início, podemos garantir que ele esteja pronto antes do próximo bloco estático. A estratégia CouchbaseContainer Waiting faz pesquisas no status da API REST do Couchbase e verifica se o estado do nó está saudável para considerá-lo pronto e parar de esperar. Se quiser obter mais detalhes sobre o CouchbaseContainer, consulte o código aqui e também no anterior blog postagens sobre o Couchbase e o TestContainers.
A próxima etapa é iniciar o backend Java, já que o Couchbase está pronto:
1 2 3 4 5 6 7 8 9 10 11 |
público estático Contêiner genérico trycbBack = novo Contêiner vinculado("trycb/java:latest") .comLinkToContainer(couchbaseContainer, "couchbase") .comExposedPorts(8080) .comCommand("-Dspring.couchbase.bootstrap-hosts="+couchbaseContainer.getContainerIpAddress()) .waitingFor(novo HttpWaitStrategy (estratégia de espera)().forPath("/wut").forStatusCode(404)); estático { trycbBack.iniciar(); } |
Algumas coisas interessantes a serem observadas aqui. Novamente, a presença de um bloco estático que inicia o contêiner pelos mesmos motivos mencionados anteriormente. Você pode ver que estamos expondo a porta 8080 aqui. Não há necessidade de fazer isso com o CouchbaseContainer, pois ele é uma classe específica dedicada à imagem padrão do Couchbase. Dessa forma, ele tem uma configuração padrão para portas, bem como uma estratégia de espera predefinida. Para o backend, usamos um GenericContainer, portanto, precisamos especificá-los. A estratégia de espera aguardará até obter um 404 para o caminho configurado. Se ela responder algo, basicamente o servidor está ativo e o Frontend pode usá-lo. Você também verá que o parâmetro Java que define o endereço do Couchbase é fornecido e o valor é obtido da instância do CouchbaseContainer.
A última informação muito útil é a chamada para o método withLinkToContainer. Ele não faz parte da API GenericContainer, não sei bem por quê. Tive que estendê-lo:
1 2 3 4 5 6 7 8 9 10 11 |
público classe Contêiner vinculado> se estende Contêiner genérico { público Contêiner vinculado(Cordas nome) { super(nome); } público SELF comLinkToContainer(LinkableContainer outro contêiner, Cordas pseudônimo) { addLink(outro contêiner, pseudônimo); retorno autônomo(); } |
Tudo o que ele faz é garantir que haverá um link do Docker entre o CouchbaseContainer e o backemd. Dessa forma, é possível garantir que os contêineres possam se comunicar uns com os outros.
Agora que o Couchbase e o backend estão prontos, podemos iniciar o frontend. Trata-se de um aplicativo Angular2 dentro do contêiner padrão do nginx. Ele precisa acessar o backend, portanto, precisamos adicionar um link ao contêiner anterior. Algo que você precisa garantir é a possibilidade de alterar a URL do servidor de backend dinamicamente em seu aplicativo Angular. Isso pode ser feito facilmente com Environments. Essa imagem de teste foi criada com os ambientes de teste e, portanto, procure o nome de host trycbBack, que é o nome fornecido no método withLinkToContainer. E, novamente, iniciamos o contêiner em um bloco estático.
1 2 3 4 5 6 |
público estático Contêiner genérico trycbFront = novo Contêiner vinculado("trycb/front:latest").comLinkToContainer(trycbBack, "trycbBack").comExposedPorts(80); estático { trycbFront.iniciar(); } |
Quando todos esses contêineres estiverem em funcionamento, podemos iniciar o contêiner do Selenium. Esse contêiner faz parte do projeto TestContainers e lhe dá acesso a vários recursos. Você pode decidir se quer usar o Chrome ou o Firefox e se quer que o teste seja gravado. No nosso caso, é um driver do Chrome e o teste será registrado e armazenado no arquivo de destino. Você também notará que adicionamos um link para o Frontend e o Backend. O frontend é acessado no navegador Chrome dentro do contêiner, e o aplicativo angular acessará o backend a partir dele. Portanto, é preciso garantir que ele também seja acessível a partir do contêiner do Selenium e que sua configuração CORS permita a comunicação.
1 2 3 4 5 6 7 |
@Regra de classe público estático NavegadorWebDriverContainer cromo = novo NavegadorWebDriverContainer() .comLinkToContainer(trycbFront, "trycbfront") .comLinkToContainer(trycbBack, "trycbback") .withDesiredCapabilities(Capacidades desejadas.cromo()) .withRecordingMode(NavegadorWebDriverContainer.VncRecordingMode.RECORD_ALL, novo Arquivo("alvo")); |
Agora você tem tudo configurado e pode executar o teste apresentado no início deste post. Espero que isso tenha sido útil e que agora você use o TestContainers para facilitar o teste de integração de aplicativos baseados no Couchbase.