Teste de unidade unit-testing
Este tutorial aborda a implementação de um teste de unidade que valida o comportamento do modelo do Sling do componente de linha de identificação criado no tutorial Componente personalizado.
Pré-requisitos prerequisites
Consulte as ferramentas e instruções necessárias para configurar um ambiente de desenvolvimento local.
Se o Java™ 8 e o Java™ 11 estiverem instalados no sistema, o executor dos testes do VS Code poderá escolher o menor tempo de execução do Java™ ao executar os testes, resultando em falhas nos testes. Se isso ocorrer, desinstale o Java™ 8.
Projeto inicial
Confira o código de linha de base no qual o tutorial se baseia:
-
Confira a ramificação
tutorial/unit-testing-startdo GitHubcode language-shell $ cd aem-guides-wknd $ git checkout tutorial/unit-testing-start -
Implante a base de código em uma instância do AEM local, usando as suas habilidades do Maven:
code language-shell $ mvn clean install -PautoInstallSinglePackagenote note NOTE Se estiver usando o AEM 6.5 ou 6.4, anexe o perfil classica qualquer comando do Maven.code language-shell $ mvn clean install -PautoInstallSinglePackage -Pclassic
É sempre possível exibir o código concluído no GitHub ou conferir o código localmente, alternando-se para a ramificação tutorial/unit-testing-start.
Objetivo
- Noções básicas sobre o teste de unidade.
- Aprender sobre as estruturas e ferramentas usadas normalmente para testar o código do AEM.
- Entender as opções de emular ou simular recursos do AEM ao gravar testes de unidade.
Fundo unit-testing-background
Neste tutorial, veremos como criar testes de unidade para o modelo do Sling do componente de linha de identificação (criado em Criar um componente personalizado do AEM). Testes de unidade são testes de tempo de compilação escritos em Java™ que verificam o comportamento esperado do código Java™. Cada teste de unidade geralmente é pequeno e valida a saída de um método (ou unidades de trabalho) em relação aos resultados esperados.
Usamos as práticas recomendadas do AEM e empregamos:
Teste de unidade e Adobe Cloud Manager unit-testing-and-adobe-cloud-manager
O Adobe Cloud Manager integra a execução de testes de unidade e a geração de relatórios de cobertura do código ao seu pipeline de CI/CD para ajudar a incentivar e promover a prática recomendada de teste de unidade do código do AEM.
Embora o código dos testes de unidade seja uma boa prática para qualquer base de código, ao usar o Cloud Manager, é importante aproveitar seus recursos de geração de relatórios e testes de qualidade do código, que oferecem testes de unidade para execução pelo Cloud Manager.
Atualizar as dependências do Maven de teste inspect-the-test-maven-dependencies
A primeira etapa é inspecionar as dependências do Maven para permitir a gravação e execução dos testes. São necessárias quatro dependências:
- JUnit5
- Estrutura de teste do Mockito
- Simulações do Sling do Apache
- Estrutura de teste de simulações do AEM (pelo io.wcm)
As dependências de teste JUnit5, **Mockito e Simulações do AEM são adicionadas automaticamente ao projeto durante a instalação com o arquétipo do Maven do AEM.
-
Para exibir essas dependências, abra o POM do reator primário em aem-guides-wknd/pom.xml, navegue até
<dependencies>..</dependencies>e exiba as dependências de JUnit, Mockito, simulações do Sling do Apache e testes de simulação do AEM do io.wcm em<!-- Testing -->. -
Verifique se
io.wcm.testing.aem-mock.junit5está definido como 4.1.0:code language-xml <dependency> <groupId>io.wcm</groupId> <artifactId>io.wcm.testing.aem-mock.junit5</artifactId> <version>4.1.0</version> <scope>test</scope> </dependency>note caution CAUTION O arquétipo 35 gera o projeto com io.wcm.testing.aem-mock.junit5versão 4.1.8. Faça o downgrade para 4.1.0 a fim de prosseguir com o restante deste capítulo. -
Abra aem-guides-wknd/core/pom.xml e veja se as dependências de teste correspondentes estão disponíveis.
Uma pasta de origem paralela no projeto principal conterá os testes de unidade e quaisquer arquivos de teste de suporte. Esta pasta de teste fornece uma separação de classes de teste do código-fonte, mas permite que os testes atuem como se estivessem nos mesmos pacotes que o código-fonte.
Criar o teste com JUnit creating-the-junit-test
Os testes de unidade normalmente mapeiam de 1 para 1 com classes de Java™. Neste capítulo, escreveremos um teste de JUnit para o BylineImpl.java, que é o modelo do Sling que oferece compatibildiade com o componente de linha de identificação.
Local onde os testes de unidade estão armazenados.
-
Crie um teste de unidade para o
BylineImpl.java, criando uma nova classe de Java™ emsrc/test/java, em uma estrutura de pasta de pacote de Java™ que espelhe o local da classe de Java™ a ser testada.
Já que estamos testando
src/main/java/com/adobe/aem/guides/wknd/core/models/impl/BylineImpl.java
a criação de uma classe de Java™ de teste de unidade correspondente em
src/test/java/com/adobe/aem/guides/wknd/core/models/impl/BylineImplTest.java
O sufixo
Testno arquivo de teste unitário, em queBylineImplTest.javaé uma convenção, que nos permite- Identificá-lo facilmente como o arquivo de teste para
BylineImpl.java - Além disso, diferenciar o arquivo de teste de a classe que está sendo testada,
BylineImpl.java
Revisar o BylineImplTest.java reviewing-bylineimpltest-java
Neste ponto, o arquivo de teste de JUnit é uma classe de Java™ vazia.
-
Atualize o arquivo com o seguinte código:
code language-java package com.adobe.aem.guides.wknd.core.models.impl; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class BylineImplTest { @BeforeEach void setUp() throws Exception { } @Test void testGetName() { fail("Not yet implemented"); } @Test void testGetOccupations() { fail("Not yet implemented"); } @Test void testIsEmpty() { fail("Not yet implemented"); } } -
O primeiro método
public void setUp() { .. }é anotado com o@BeforeEachde JUnit, que instrui o executor de teste de JUnit a executar este método antes de executar cada método de teste nesta classe. Isso fornece um local útil para inicializar o estado de teste comum exigido por todos os testes. -
Os métodos subsequentes são os métodos de teste, cujos nomes são prefixados com
testpor convenção e marcados com a anotação@Test. Observe que, por padrão, todos os nossos testes estão definidos como reprovados, pois ainda não os implementamos.Para começar, usamos apenas um método de teste para cada método público na classe que estamos testando; portanto:
table 0-row-3 1-row-3 2-row-3 3-row-3 BylineImpl.java BylineImplTest.java getName() é testado por testGetName() getOccupations() é testado por testGetOccupations() isEmpty() é testado por testIsEmpty() Esses métodos podem ser expandidos conforme necessário, como veremos adiante neste capítulo.
Quando esta classe de teste de JUnit (também conhecida como caso de teste de JUnit) é executada, cada método marcado com o
@Testserá executado como um teste que pode ser aprovado ou reprovado.
core/src/test/java/com/adobe/aem/guides/wknd/core/models/impl/BylineImplTest.java
-
Execute o caso de teste de JUnit, clicando com o botão direito do mouse no arquivo
BylineImplTest.javae tocando em Executar.
Como esperado, todos os testes falham, pois ainda não foram implementados.
Clique com o botão direito do mouse em BylineImplTests.java > Executar
Revisar o BylineImpl.java reviewing-bylineimpl-java
Ao escrever testes de unidade, há duas abordagens principais:
- TDD ou desenvolvimento orientado por teste, que envolve gravar os testes de unidade de forma incremental, imediatamente antes de a implementação ser desenvolvida; gravar um teste; e gravar a implementação para fazer com que o teste passe.
- Desenvolvimento com implementação primeiro, que envolve o desenvolvimento do código de trabalho primeiro e, em seguida, a gravação de testes que validam esse código.
Neste tutorial, a última abordagem será usada (já que criamos um BylineImpl.java de trabalho em um capítulo anterior). Por isso, precisamos revisar e entender os comportamentos de seus métodos públicos, mas também alguns de seus detalhes de implementação. Isso pode parecer contraintuitivo, pois um bom teste deve se preocupar apenas com as entradas e saídas, mas, ao trabalhar no AEM, há várias considerações de implementação que precisam ser entendidas a fim de construir testes de trabalho.
O TDD no contexto do AEM requer um nível de conhecimento e é melhor adotado por desenvolvedores do AEM proficientes em desenvolvimento no AEM e testes de unidade do código do AEM.
Configurar o contexto de teste do AEM setting-up-aem-test-context
A maioria dos códigos escritos para o AEM depende de APIs do JCR, Sling ou AEM, que, por sua vez, exigem o contexto do AEM em execução para ser executados corretamente.
Como os testes de unidade são executados na criação, fora do contexto de uma instância do AEM em execução, esse contexto não existe. Para facilitar isso, as simulações do AEM do wcm.io cria um contexto de simulação que permite que essas APIs em sua maioria atuem como se estivessem em execução no AEM.
-
Crie um contexto do AEM, usando o
AemContextdo wcm.io no BylineImplTest.java e adicionando-o como uma extensão de JUnit decorada com@ExtendWithao arquivo BylineImplTest.java. A extensão cuida de todas as tarefas de inicialização e limpeza necessárias. Crie uma variável de classe paraAemContextque possa ser usada para todos os métodos de teste.code language-java import org.junit.jupiter.api.extension.ExtendWith; import io.wcm.testing.mock.aem.junit5.AemContext; import io.wcm.testing.mock.aem.junit5.AemContextExtension; ... @ExtendWith(AemContextExtension.class) class BylineImplTest { private final AemContext ctx = new AemContext();Esta variável,
ctx, expõe um contexto de simulação do AEM que fornece algumas abstrações do AEM e do Sling:- O modelo do Sling BylineImpl está registrado neste contexto
- As estruturas de conteúdo de JCR de simulação são criadas neste contexto
- Os serviços da OSGi personalizados podem ser registrados neste contexto
- Fornece vários objetos de modelo de simulação e auxiliares comuns necessários, como objetos SlingHttpServletRequest, vários serviços de simulação da OSGi para Sling e AEM, como ModelFactory, PageManager, Page, Template, ComponentManager, Component, TagManager, Tag etc.
- Nem todos os métodos para esses objetos foram implementados.
- E muito mais!
O objeto
ctxatuará como ponto de entrada para a maioria do nosso contexto de simulação. -
No método
setUp(..), que é executado antes de cada método@Test, defina um estado de teste de simulação comum:code language-java @BeforeEach public void setUp() throws Exception { ctx.addModelsForClasses(BylineImpl.class); ctx.load().json("/com/adobe/aem/guides/wknd/core/models/impl/BylineImplTest.json", "/content"); }addModelsForClassesregistra o modelo do Sling a ser testado no contexto do AEM de simulação, para que possa ser instanciado nos métodos@Test.- O
load().jsoncarrega estruturas de recursos no contexto de simulação, permitindo que o código interaja com esses recursos como se fossem fornecidos por um repositório real. As definições dos recursos no arquivoBylineImplTest.jsonsão carregadas no contexto do JCR de simulação em /content. BylineImplTest.jsonainda não existe; portanto, vamos criá-lo e definir as estruturas de recursos do JCR necessárias para o teste.
-
Os arquivos JSON que representam as estruturas de recursos de simulação são armazenados em core/src/test/resources, seguindo o mesmo caminho de pacote do arquivo de teste JUnit Java™.
Crie um arquivo JSON em
core/test/resources/com/adobe/aem/guides/wknd/core/models/implchamado BylineImplTest.json com o seguinte conteúdo:code language-json { "byline": { "jcr:primaryType": "nt:unstructured", "sling:resourceType": "wknd/components/content/byline" } }
Esse JSON define um recurso de simulação (nó do JCR) para o teste de unidade do componente de linha de identificação. Neste ponto, o JSON conta com o conjunto mínimo de propriedades necessárias para representar um recurso de conteúdo do componente de linha de identificação, o
jcr:primaryTypeesling:resourceType.Uma regra geral ao trabalhar com testes de unidade é criar o conjunto mínimo de conteúdo de simulação, contexto e código necessários para satisfazer cada teste. Evite a tentação de criar um contexto de simulação completo antes de escrever os testes, pois isso geralmente resulta em artefatos desnecessários.
Agora, com a existência do BylineImplTest.json, quando
ctx.json("/com/adobe/aem/guides/wknd/core/models/impl/BylineImplTest.json", "/content")for executado, as definições de recursos de simulação serão carregadas no contexto no caminho /content.
Testar getName() testing-get-name
Agora que temos uma configuração básica de contexto de simulação, vamos escrever o nosso primeiro teste para BylineImpl's getName(). Esse teste precisa garantir que o método getName() retorne o nome criado corretamente armazenado na propriedade “nome” do recurso.
-
Atualize o método testGetName() no BylineImplTest.java da seguinte maneira:
code language-java import com.adobe.aem.guides.wknd.core.models.Byline; ... @Test public void testGetName() { final String expected = "Jane Doe"; ctx.currentResource("/content/byline"); Byline byline = ctx.request().adaptTo(Byline.class); String actual = byline.getName(); assertEquals(expected, actual); }String expecteddefine o valor esperado. Nós o definiremos como “Jane Doe”.ctx.currentResourcedefine o contexto do recurso de simulação para avaliar o código em relação a ele; portanto, é definido como /content/byline, pois é aí que o recurso de conteúdo de linha de identificação de simulação é carregado.Byline bylineinstancia o modelo do Sling de linha de identificação, adaptando-o do objeto de solicitação de simulação.String actualinvoca o método que estamos testando,getName(), no objeto de modelo do Sling de linha de identificação.assertEqualsafirma que o valor esperado corresponde ao valor retornado pelo objeto de modelo do Sling de linha de identificação. Se esses valores não forem iguais, o teste falhará.
-
Execute o teste… e ele falhará com um
NullPointerException.Esse teste NÃO falha, porque nunca definimos uma propriedade
nameno JSON de simulação, que fará com que o teste falhe, mas a execução do teste não chegou a esse ponto. Esse teste falha devido a umNullPointerExceptionno próprio objeto de linha de identificação. -
No
BylineImpl.java, se@PostConstruct init()acionar uma exceção, ela impedirá que o modelo do Sling seja instanciado e fará com que esse objeto de modelo do Sling seja nulo.code language-java @PostConstruct private void init() { image = modelFactory.getModelFromWrappedRequest(request, request.getResource(), Image.class); }Acontece que, embora o serviço ModelFactory seja da OSGi fornecido por meio do
AemContext(através do contexto do Sling do Apache), nem todos os métodos são implementados, incluindogetModelFromWrappedRequest(...), que é chamado no métodoinit()do BylineImpl. Isso resulta em um AbstractMethodError, que, por sua vez, causa uma falha eminit(), e a adaptação resultante doctx.request().adaptTo(Byline.class)é um objeto nulo.Como as simulações fornecidas não podem acomodar o nosso código, precisamos implementar o contexto de simulação por conta própria. Para isso, podemos usar o Mockito para criar um objeto ModelFactory de simulação, que retorna um objeto de imagem de simulação quando
getModelFromWrappedRequest(...)é invocado nele.Como, para instanciar o modelo do Sling de linha de identificação, esse contexto de simulação precisa estar presente, podemos adicioná-lo ao método
@Before setUp(). Também precisamos adicionarMockitoExtension.classà anotação@ExtendWithacima da classe BylineImplTest.code language-java package com.adobe.aem.guides.wknd.core.models.impl; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.Mock; import com.adobe.aem.guides.wknd.core.models.Byline; import com.adobe.cq.wcm.core.components.models.Image; import io.wcm.testing.mock.aem.junit5.AemContext; import io.wcm.testing.mock.aem.junit5.AemContextExtension; import org.apache.sling.models.factory.ModelFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; import org.apache.sling.api.resource.Resource; @ExtendWith({ AemContextExtension.class, MockitoExtension.class }) public class BylineImplTest { private final AemContext ctx = new AemContext(); @Mock private Image image; @Mock private ModelFactory modelFactory; @BeforeEach public void setUp() throws Exception { ctx.addModelsForClasses(BylineImpl.class); ctx.load().json("/com/adobe/aem/guides/wknd/core/models/impl/BylineImplTest.json", "/content"); lenient().when(modelFactory.getModelFromWrappedRequest(eq(ctx.request()), any(Resource.class), eq(Image.class))) .thenReturn(image); ctx.registerService(ModelFactory.class, modelFactory, org.osgi.framework.Constants.SERVICE_RANKING, Integer.MAX_VALUE); } @Test void testGetName() { ... }@ExtendWith({AemContextExtension.class, MockitoExtension.class})marca a classe de caso de teste a ser executada com a extensão Mockito JUnit Jupiter, que permite o uso das anotações @Mock para definir objetos de simulação na camada da classe.@Mock private Imagecria um objeto de simulação do tipocom.adobe.cq.wcm.core.components.models.Image. Isso é definido na camada da classe, de modo que, conforme necessário, os métodos@Testpossam alterar seu comportamento.@Mock private ModelFactorycria um objeto de simulação do tipo ModelFactory. Esta é uma simulação pura do Mockito e não contém nenhum método implementado. Isso é definido na camada da classe, de modo que, conforme necessário, os métodos@Testpossam alterar seu comportamento.when(modelFactory.getModelFromWrappedRequest(..)registra o comportamento da simulação para quandogetModelFromWrappedRequest(..)é chamado no objeto ModelFactory de simulação. O resultado definido emthenReturn (..)é retornar o objeto de imagem de simulação. Esse comportamento só é chamado quando: o primeiro parâmetro é igual ao objeto de solicitação dectx, o segundo parâmetro é qualquer objeto de recurso, e o terceiro parâmetro precisa ser a classe de imagem dos componentes principais. Aceitamos qualquer recurso, pois, em todos os nossos testes, estamos configurando octx.currentResource(...)para vários recursos de simulação definidos no BylineImplTest.json. Observe que adicionamos a rigidez lenient() porque vamos querer substituir esse comportamento do ModelFactory posteriormente.ctx.registerService(..). registra o objeto ModelFactory de simulação no AemContext, com a classificação de serviço mais alta. Isso é necessário, pois o ModelFactory usado noinit()do BylineImpl é inserido por meio do campo@OSGiService ModelFactory model. Para que o AemContext injete o nosso objeto de simulação, que manipula chamadas paragetModelFromWrappedRequest(..), preicsamos registrá-lo como o serviço de classificação mais alta desse tipo (ModelFactory).
-
Execute novamente o teste e, novamente, ele falhará, mas, desta vez, a mensagem de por que ele falhou estará clara.
Falha de testGetName() devido à asserção
Recebemos um AssertionError, que significa que a condição de asserção do teste falhou e nos informa que o valor esperado é “Jane Doe”, mas o valor real é nulo. Isso faz sentido, porque a propriedade “nome” não foi adicionada à definição de recurso de simulação /content/byline em BylineImplTest.json; então, vamos adicioná-la:
-
Atualize o BylineImplTest.json para definir
"name": "Jane Doe".code language-json { "byline": { "jcr:primaryType": "nt:unstructured", "sling:resourceType": "wknd/components/content/byline", "name": "Jane Doe" } } -
Execute o teste novamente, e
testGetName()agora será aprovado!
Teste de getOccupations() testing-get-occupations
Muito bem! O primeiro teste foi aprovado. Vamos prosseguir e testar getOccupations(). Como a inicialização do contexto de simulação foi feita no método @Before setUp(), ela está disponível para todos os métodos @Test neste caso de teste, incluindo getOccupations().
Lembre-se de que esse método precisa retornar uma lista de profissões classificada em ordem alfabética (decrescente) armazenada na propriedade de profissões.
-
Atualize
testGetOccupations()da seguinte maneira:code language-java import java.util.List; import com.google.common.collect.ImmutableList; ... @Test public void testGetOccupations() { List<String> expected = new ImmutableList.Builder<String>() .add("Blogger") .add("Photographer") .add("YouTuber") .build(); ctx.currentResource("/content/byline"); Byline byline = ctx.request().adaptTo(Byline.class); List<String> actual = byline.getOccupations(); assertEquals(expected, actual); }List<String> expecteddefine o resultado esperado.ctx.currentResourcedefine o recurso atual para avaliar o contexto em relação à definição do recurso de simulação em /content/byline. Isso garante que o BylineImpl.java seja executado no contexto do nosso recurso de simulação.ctx.request().adaptTo(Byline.class)instancia o modelo do Sling de linha de identificação, adaptando-o do objeto de solicitação de simulação.byline.getOccupations()invoca o método que estamos testando,getOccupations(), no objeto de modelo do Sling de linha de identificação.assertEquals(expected, actual)afirma que a lista esperada é igual à lista real.
-
Lembre-se de que, assim como
getName()acima, o BylineImplTest.json não define profissões. Portanto, este teste falhará se o executarmos, já quebyline.getOccupations()retornará uma lista vazia.Atualize BylineImplTest.json para incluir uma lista de profissões, e elas serão definidas em ordem não alfabética para garantir que nossos testes confirmem que as profissões estão classificadas em ordem alfabética por
getOccupations().code language-json { "byline": { "jcr:primaryType": "nt:unstructured", "sling:resourceType": "wknd/components/content/byline", "name": "Jane Doe", "occupations": ["Photographer", "Blogger", "YouTuber"] } } -
Execute o teste; novamente, passamos! Parece que as profissões ordenadas estão funcionando.
aprovações de testGetOccupations()
Testar isEmpty() testing-is-empty
O último método para testar isEmpty().
Testar isEmpty() é interessante, pois envolve testar várias condições. Ao revisar o método isEmpty() do BylineImpl.java, as seguintes condições precisam ser testadas:
- Retorna verdadeiro quando o nome está vazio
- Retorna verdadeiro quando as profissões são nulas ou estão vazias
- Retorna verdadeiro quando a imagem é nula ou não tem o URL de origem
- Retorna falso quando o nome, as profissões e a imagem (com um URL de origem) estão presentes
Para isso, precisamos criar métodos de teste, cada um testando uma condição específica, e novas estruturas de recursos de simulação em BylineImplTest.json para conduzir esses testes.
Essa verificação permitiu-nos ignorar o teste para quando getName(), getOccupations() e getImage() estão vazios, pois o comportamento esperado desse estado é testado via isEmpty().
-
O primeiro teste analisará a condição de um componente totalmente novo que não tem propriedades definidas.
Adicione uma nova definição de recurso a
BylineImplTest.json, dando-lhe o nome semântico “vazio”.code language-json { "byline": { "jcr:primaryType": "nt:unstructured", "sling:resourceType": "wknd/components/content/byline", "name": "Jane Doe", "occupations": ["Photographer", "Blogger", "YouTuber"] }, "empty": { "jcr:primaryType": "nt:unstructured", "sling:resourceType": "wknd/components/content/byline" } }"empty": {...}define uma nova definição de recurso chamada “vazio” que tenha somentejcr:primaryTypeesling:resourceType.Lembre-se de que carregamos
BylineImplTest.jsonemctxantes da execução de cada método de teste em@setUp. Portanto, essa nova definição do recurso fica imediatamente disponível para nós nos testes em /content/empty. -
Atualize
testIsEmpty()da maneira a seguir, definindo o recurso atual com a nova definição do recurso de simulação “vazio”.code language-java @Test public void testIsEmpty() { ctx.currentResource("/content/empty"); Byline byline = ctx.request().adaptTo(Byline.class); assertTrue(byline.isEmpty()); }Execute o teste e verifique se ele foi aprovado.
-
Em seguida, crie um conjunto de métodos para garantir que, se qualquer um dos pontos de dados necessários (nome, profissões ou imagem) estiver vazio,
isEmpty()retornará verdadeiro.Para cada teste, uma definição de recurso de simulação diferente é usada; atualize BylineImplTest.json com as definições de recursos adicionais para without-name e without-occupations.
code language-json { "byline": { "jcr:primaryType": "nt:unstructured", "sling:resourceType": "wknd/components/content/byline", "name": "Jane Doe", "occupations": ["Photographer", "Blogger", "YouTuber"] }, "empty": { "jcr:primaryType": "nt:unstructured", "sling:resourceType": "wknd/components/content/byline" }, "without-name": { "jcr:primaryType": "nt:unstructured", "sling:resourceType": "wknd/components/content/byline", "occupations": "[Photographer, Blogger, YouTuber]" }, "without-occupations": { "jcr:primaryType": "nt:unstructured", "sling:resourceType": "wknd/components/content/byline", "name": "Jane Doe" } }Crie os métodos de teste a seguir para testar cada um desses estados.
code language-java @Test public void testIsEmpty() { ctx.currentResource("/content/empty"); Byline byline = ctx.request().adaptTo(Byline.class); assertTrue(byline.isEmpty()); } @Test public void testIsEmpty_WithoutName() { ctx.currentResource("/content/without-name"); Byline byline = ctx.request().adaptTo(Byline.class); assertTrue(byline.isEmpty()); } @Test public void testIsEmpty_WithoutOccupations() { ctx.currentResource("/content/without-occupations"); Byline byline = ctx.request().adaptTo(Byline.class); assertTrue(byline.isEmpty()); } @Test public void testIsEmpty_WithoutImage() { ctx.currentResource("/content/byline"); lenient().when(modelFactory.getModelFromWrappedRequest(eq(ctx.request()), any(Resource.class), eq(Image.class))).thenReturn(null); Byline byline = ctx.request().adaptTo(Byline.class); assertTrue(byline.isEmpty()); } @Test public void testIsEmpty_WithoutImageSrc() { ctx.currentResource("/content/byline"); when(image.getSrc()).thenReturn(""); Byline byline = ctx.request().adaptTo(Byline.class); assertTrue(byline.isEmpty()); }testIsEmpty()testa com a definição do recurso de simulação vazio e declara queisEmpty()é verdadeiro.testIsEmpty_WithoutName()testa em relação a uma definição do recurso de simulação que tem profissões, mas nenhum nome.testIsEmpty_WithoutOccupations()testa em relação a uma definição do recurso de simulação que tem um nome, mas nenhuma profissão.testIsEmpty_WithoutImage()testa uma definição do recurso de modelo com um nome e profissões, mas define a imagem de simulação para retornar como nulo. Observe que queremos substituir o comportamentomodelFactory.getModelFromWrappedRequest(..)definido emsetUp()para garantir que o objeto de imagem retornado por esta chamada seja nulo. O recurso de canhotos do Mockito é estrito e não permite um código duplicado. Portanto, definimos a simulação com configuraçõeslenientpara observar explicitamente que estamos substituindo o comportamento no métodosetUp().testIsEmpty_WithoutImageSrc()testa uma definição de recurso de simulação com um nome e profissões, mas define o modelo de imagem para retornar uma string em branco quandogetSrc()for invocado. -
Por fim, escreva um teste para garantir que isEmpty() retorne falso quando o componente estiver configurado corretamente. Para esta condição, podemos reutilizar /content/byline, que representa um componente de linha de identificação totalmente configurado.
code language-java @Test public void testIsNotEmpty() { ctx.currentResource("/content/byline"); when(image.getSrc()).thenReturn("/content/bio.png"); Byline byline = ctx.request().adaptTo(Byline.class); assertFalse(byline.isEmpty()); } -
Agora, execute todos os testes de unidade no arquivo BylineImplTest.java e revise o resultado do relatório de teste de Java™.
Execução de testes de unidade como parte da compilação running-unit-tests-as-part-of-the-build
Os testes de unidade são executados e devem ser aprovados como parte da compilação do Maven. Isso garante que todos os testes sejam aprovados com sucesso antes da implantação de um aplicativo. A execução de metas do Maven, como pacote ou instalação, chama automaticamente e requer a aprovação de todos os testes de unidade do projeto.
$ mvn package
$ mvn package
Da mesma forma, se alterarmos um método de teste para que falhe, a compilação falhará e relatará qual teste falhou, bem como o porquê.
Revisar o código review-the-code
Veja o código concluído no GitHub ou revise e implante o código localmente na ramificação do Git tutorial/unit-testing-solution.