Test di unità unit-testing
Questa esercitazione descrive l'implementazione di uno unit test che convalida il comportamento del modello Sling del componente Byline, creato nell'esercitazione Componente personalizzato.
Prerequisiti prerequisites
Esaminare gli strumenti e le istruzioni necessari per configurare un ambiente di sviluppo locale.
Se nel sistema sono installati sia Java™ 8 che Java™ 11, l'esecuzione dei test di VS Code potrebbe scegliere il runtime Java™ inferiore durante l'esecuzione dei test, causando errori di test. In questo caso, disinstallare Java™ 8.
Progetto iniziale
Consulta il codice della riga di base su cui si basa l’esercitazione:
-
Controlla il ramo
tutorial/unit-testing-start
da GitHubcode language-shell $ cd aem-guides-wknd $ git checkout tutorial/unit-testing-start
-
Implementa la base di codice in un’istanza AEM locale utilizzando le tue competenze Maven:
code language-shell $ mvn clean install -PautoInstallSinglePackage
note note NOTE Se utilizzi AEM 6.5 o 6.4, aggiungi il profilo classic
a qualsiasi comando Maven.code language-shell $ mvn clean install -PautoInstallSinglePackage -Pclassic
Puoi sempre visualizzare il codice finito su GitHub o estrarre il codice localmente passando al ramo tutorial/unit-testing-start
.
Obiettivo
- Comprendere le nozioni di base sul testing di unità.
- Scopri i framework e gli strumenti comunemente utilizzati per testare il codice AEM.
- Comprendi le opzioni per deridere o simulare le risorse AEM durante la scrittura degli unit test.
Informazioni di base unit-testing-background
In questa esercitazione verrà illustrato come scrivere unit test per il modello Sling del componente Byline (creato in Creazione di un componente AEM personalizzato). Gli unit test sono test di build-time scritti in Java™ che verificano il comportamento previsto del codice Java™. Ogni unit test è in genere piccolo e convalida l'output di un metodo (o unità di lavoro) in base ai risultati previsti.
Utilizziamo le best practice per l’AEM e utilizziamo:
- JUnit 5
- Framework di test Mockito
- wcm.io Test Framework (che si basa su Apache Sling Mocks)
Test di unità e Cloud Manager Adobe unit-testing-and-adobe-cloud-manager
Adobe Cloud Manager integra l'esecuzione di unit test e il reporting sulla copertura del codice nella propria pipeline CI/CD per incoraggiare e promuovere la best practice per il codice AEM di unit test.
Anche se il codice di test di unità è una buona pratica per qualsiasi base di codice, quando si utilizza Cloud Manager è importante sfruttare i suoi servizi di test e reporting della qualità del codice fornendo unit test per l’esecuzione di Cloud Manager.
Aggiornare le dipendenze Maven del test inspect-the-test-maven-dependencies
Il primo passaggio consiste nell’esaminare le dipendenze Maven per supportare la scrittura e l’esecuzione dei test. Sono necessarie quattro dipendenze:
- JUnit5
- Framework di prova Mockito
- Apache Sling Mocks
- Framework di test AEM-Mocks (di io.wcm)
Le dipendenze dei test JUnit5, Mockito e AEM Mocks** vengono aggiunte automaticamente al progetto durante l'installazione utilizzando l'archetipo Maven AEM.
-
Per visualizzare queste dipendenze, apri il POM Reactor padre all'indirizzo aem-guides-wknd/pom.xml, passa a
<dependencies>..</dependencies>
e visualizza le dipendenze per JUnit, Mockito, Apache Sling Mocks e AEM Mock Test di io.wcm in<!-- Testing -->
. -
Verificare che
io.wcm.testing.aem-mock.junit5
sia impostato su 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 Archetipo 35 genera il progetto con io.wcm.testing.aem-mock.junit5
versione 4.1.8. Effettua il downgrade a 4.1.0 per seguire il resto di questo capitolo. -
Apri aem-guides-wknd/core/pom.xml e osserva che le dipendenze di test corrispondenti sono disponibili.
Una cartella di origine parallela nel progetto core conterrà gli unit test ed eventuali file di test di supporto. Questa cartella test fornisce la separazione delle classi di test dal codice sorgente, ma consente ai test di agire come se si trovassero negli stessi pacchetti del codice sorgente.
Creazione del test JUnit creating-the-junit-test
Gli unit test tipicamente mappano 1-a-1 con le classi Java™. In questo capitolo verrà scritto un test JUnit per BylineImpl.java, che è il modello Sling che supporta il componente Byline.
Percorso in cui sono archiviati gli unit test.
-
Creare uno unit test per
BylineImpl.java
creando una nuova classe Java™ insrc/test/java
in una struttura di cartelle di pacchetti Java™ che rispecchia la posizione della classe Java™ da testare.Poiché stiamo testando
src/main/java/com/adobe/aem/guides/wknd/core/models/impl/BylineImpl.java
crea una classe Java™ unit test corrispondente in
src/test/java/com/adobe/aem/guides/wknd/core/models/impl/BylineImplTest.java
Il suffisso
Test
nel file di unit testBylineImplTest.java
è una convenzione che consente di- Identificarlo facilmente come file di test per
BylineImpl.java
- Ma anche differenziare il file di test da la classe in fase di test,
BylineImpl.java
Revisione di BylineImplTest.java reviewing-bylineimpltest-java
A questo punto, il file di test JUnit è una classe Java™ vuota.
-
Aggiorna il file con il seguente codice:
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"); } }
-
Il primo metodo
public void setUp() { .. }
è annotato con JUnit@BeforeEach
, che indica al runner di test JUnit di eseguire questo metodo prima di eseguire ogni metodo di test in questa classe. In questo modo è possibile inizializzare lo stato di test comune richiesto da tutti i test. -
I metodi successivi sono i metodi di test, i cui nomi sono preceduti da
test
per convenzione e contrassegnati con l'annotazione@Test
. Per impostazione predefinita, tutti i nostri test sono impostati per non riuscire, poiché non li abbiamo ancora implementati.Per iniziare, iniziamo con un singolo metodo di test per ogni metodo pubblico sulla classe che stiamo testando, quindi:
table 0-row-3 1-row-3 2-row-3 3-row-3 BylineImpl.java BylineImplTest.java getName() è testato da testGetName() getOccupations() è testato da testGetOccupations() isEmpty() è testato da testIsEmpty() Questi metodi possono essere ampliati in base alle esigenze, come vedremo più avanti in questo capitolo.
Quando viene eseguita questa classe di test JUnit (nota anche come test case JUnit), ogni metodo contrassegnato con
@Test
verrà eseguito come test che può essere superato o non superato.
core/src/test/java/com/adobe/aem/guides/wknd/core/models/impl/BylineImplTest.java
-
Eseguire il test case JUnit facendo clic con il pulsante destro del mouse sul file
BylineImplTest.java
e toccando Esegui.
Come previsto, tutti i test hanno esito negativo poiché non sono ancora stati implementati.Fare clic con il pulsante destro del mouse su BylineImplTests.java > Esegui
Revisione di BylineImpl.java reviewing-bylineimpl-java
Durante la scrittura degli unit test, esistono due approcci principali:
- TDD o sviluppo basato su test, che prevede la scrittura degli unit test in modo incrementale, immediatamente prima dello sviluppo dell'implementazione; scrivere un test, scrivere l'implementazione per far sì che il test venga superato.
- Implementazione-prima Sviluppo, che comporta lo sviluppo del codice di lavoro prima e la scrittura di test che convalidano tale codice.
In questo tutorial viene utilizzato quest'ultimo approccio (in quanto è già stato creato un BylineImpl.java funzionante in un capitolo precedente). Per questo motivo, dobbiamo rivedere e comprendere i comportamenti dei suoi metodi pubblici, ma anche alcuni dei suoi dettagli di implementazione. Ciò può sembrare contrario, poiché un buon test dovrebbe preoccuparsi solo degli input e degli output, tuttavia quando si lavora in AEM, vi sono varie considerazioni di implementazione che devono essere comprese per costruire test di funzionamento.
Il TDD nel contesto dell'AEM richiede un livello di competenza ed è meglio se adottato da sviluppatori dell'AEM esperti nello sviluppo dell'AEM e nel test di unità del codice AEM.
Impostazione del contesto dei test AEM setting-up-aem-test-context
La maggior parte del codice scritto per l’AEM si basa sulle API JCR, Sling o AEM, che a loro volta richiedono il contesto di un AEM in esecuzione per essere eseguito correttamente.
Poiché gli unit test vengono eseguiti al momento della compilazione, al di fuori del contesto di un’istanza AEM in esecuzione, tale contesto non esiste. Per facilitare questa fase, wcm.io's AEM Mocks crea un contesto fittizio che consente a queste API di agire principalmente come se fossero in esecuzione nell'AEM.
-
Creare un contesto AEM utilizzando wcm.io's
AemContext
in BylineImplTest.java aggiungendolo come estensione JUnit decorata con@ExtendWith
al file BylineImplTest.java. L'estensione si occupa di tutte le attività di inizializzazione e pulizia necessarie. Creare una variabile di classe perAemContext
che può essere utilizzata per tutti i metodi di test.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();
Questa variabile,
ctx
, espone un contesto AEM fittizio che fornisce alcune astrazioni AEM e Sling:- Il modello Sling BylineImpl è registrato in questo contesto
- In questo contesto vengono create strutture di contenuto JCR fittizie
- I servizi OSGi personalizzati possono essere registrati in questo contesto
- Fornisce vari oggetti fittizi e helper comuni richiesti, come oggetti SlingHttpServletRequest, vari servizi fittizi Sling e AEM OSGi come ModelFactory, PageManager, Page, Template, ComponentManager, Component, TagManager, Tag, ecc.
- Non tutti i metodi per questi oggetti sono implementati!
- E molto di più!
L'oggetto
ctx
fungerà da punto di ingresso per la maggior parte del contesto fittizio. -
Nel metodo
setUp(..)
, eseguito prima di ogni metodo@Test
, definire uno stato di test fittizio comune: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"); }
addModelsForClasses
registra il modello Sling da testare nel contesto AEM fittizio, in modo da crearne un'istanza nei metodi@Test
.load().json
carica le strutture delle risorse nel contesto fittizio, consentendo al codice di interagire con tali risorse come se fossero fornite da un archivio reale. Le definizioni delle risorse nel fileBylineImplTest.json
sono caricate nel contesto JCR fittizio in /content.BylineImplTest.json
non esiste ancora, quindi creiamolo e definiamo le strutture di risorse JCR necessarie per il test.
-
I file JSON che rappresentano le strutture di risorse fittizie sono archiviati in core/src/test/resources seguendo lo stesso percorso del pacchetto del file di test Java™ JUnit.
Crea un file JSON in
core/test/resources/com/adobe/aem/guides/wknd/core/models/impl
denominato BylineImplTest.json con il seguente contenuto:code language-json { "byline": { "jcr:primaryType": "nt:unstructured", "sling:resourceType": "wknd/components/content/byline" } }
Questo JSON definisce una risorsa fittizia (nodo JCR) per l’unit test del componente Byline. A questo punto, il JSON ha il set minimo di proprietà necessarie per rappresentare una risorsa di contenuto del componente Byline,
jcr:primaryType
esling:resourceType
.Una regola generale quando si lavora con gli unit test consiste nel creare il set minimo di contenuti fittizi, contesto e codice necessario per soddisfare ogni test. Evita la tentazione di creare un contesto fittizio completo prima di scrivere i test, in quanto spesso genera artefatti non necessari.
Ora con l'esistenza di BylineImplTest.json, quando viene eseguito
ctx.json("/com/adobe/aem/guides/wknd/core/models/impl/BylineImplTest.json", "/content")
, le definizioni delle risorse fittizie vengono caricate nel contesto nel percorso /contenuto.
Verifica di getName() testing-get-name
Ora che abbiamo configurato un contesto fittizio di base, scriviamo il primo test per BylineImpl's getName(). Questo test deve garantire che il metodo getName() restituisca il nome creato correttamente archiviato nella proprietà "name" della risorsa.
-
Aggiornare il metodo testGetName() in BylineImplTest.java come segue:
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 expected
imposta il valore previsto. Verrà impostato su "Jane Done".ctx.currentResource
imposta il contesto della risorsa fittizia su cui valutare il codice, quindi viene impostato su /content/byline in quanto è dove viene caricata la risorsa di contenuto fittizia.Byline byline
crea un'istanza del modello Sling Byline adattandolo dal finto oggetto Request.String actual
richiama il metodo in fase di test,getName()
, sull'oggetto modello Sling Byline.assertEquals
asserisce che il valore previsto corrisponde al valore restituito dall'oggetto modello Sling byline. Se questi valori non sono uguali, il test avrà esito negativo.
-
Esegui il test… e non riesce con
NullPointerException
.Il test NON ha esito negativo perché non è mai stata definita una proprietà
name
nel JSON fittizio. Il test non verrà eseguito correttamente, tuttavia l'esecuzione del test non è arrivata a questo punto. Il test non riesce a causa di unNullPointerException
nell'oggetto byline stesso. -
In
BylineImpl.java
, se@PostConstruct init()
genera un'eccezione, impedisce al modello Sling di creare un'istanza e causa l'impostazione dell'oggetto modello Sling come null.code language-java @PostConstruct private void init() { image = modelFactory.getModelFromWrappedRequest(request, request.getResource(), Image.class); }
Si è verificato che mentre il servizio OSGi ModelFactory viene fornito tramite
AemContext
(tramite il contesto Sling Apache), non tutti i metodi vengono implementati, inclusogetModelFromWrappedRequest(...)
che viene chiamato nel metodoinit()
di BylineImpl. Ciò determina un AbstractMethodError, che nel termine causa il mancato funzionamento diinit()
e l'adattamento risultante dictx.request().adaptTo(Byline.class)
è un oggetto null.Poiché gli scherzi forniti non possono contenere il nostro codice, è necessario implementare direttamente il contesto fittizio. Per questo è possibile utilizzare Mockito per creare un oggetto ModelFactory fittizio, che restituisce un oggetto Image fittizio quando
getModelFromWrappedRequest(...)
viene richiamato su di esso.Poiché per creare un'istanza del modello Sling Byline è necessario che il contesto fittizio sia attivo, è possibile aggiungerlo al metodo
@Before setUp()
. È inoltre necessario aggiungereMockitoExtension.class
all'annotazione@ExtendWith
sopra la 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})
contrassegna la classe Test Case da eseguire con l'estensione Mockito JUnit Jupiter che consente l'utilizzo delle annotazioni @Mock per definire oggetti fittizi a livello di classe.@Mock private Image
crea un oggetto fittizio di tipocom.adobe.cq.wcm.core.components.models.Image
. Viene definito a livello di classe in modo che, se necessario, i metodi@Test
possano modificarne il comportamento in base alle esigenze.@Mock private ModelFactory
crea un oggetto fittizio di tipo ModelFactory. Questa è una pura beffa di Mockito e non ha metodi implementati su di essa. Questo viene definito a livello di classe in modo che, se necessario,@Test
metodi possano modificarne il comportamento in base alle esigenze.when(modelFactory.getModelFromWrappedRequest(..)
registra il comportamento fittizio per quandogetModelFromWrappedRequest(..)
viene chiamato sull'oggetto ModelFactory fittizio. Il risultato definito inthenReturn (..)
è quello di restituire l'oggetto immagine fittizio. Questo comportamento viene richiamato solo quando: il primo parametro è uguale all'oggetto di richiesta dictx
, il secondo parametro è qualsiasi oggetto Resource e il terzo parametro deve essere la classe Immagine dei Componenti core. Accettiamo qualsiasi risorsa perché durante i test impostiamoctx.currentResource(...)
su varie risorse fittizie definite in BylineImplTest.json. Si noti che verrà aggiunta la rigidità lenient() perché in seguito si desidera ignorare questo comportamento di ModelFactory.ctx.registerService(..)
. registra l'oggetto ModelFactory fittizio in AemContext, con la classificazione di servizio più elevata. Questa operazione è necessaria perché ModelFactory utilizzato ininit()
di BylineImpl viene inserito tramite il campo@OSGiService ModelFactory model
. Affinché AemContext inserisca l'oggetto fittizio our, che gestisce le chiamate agetModelFromWrappedRequest(..)
, è necessario registrarlo come servizio di classificazione più elevata di quel tipo (ModelFactory).
-
Riesegui il test e di nuovo non riesce, ma questa volta il messaggio indica chiaramente il motivo dell’errore.
errore testGetName() a causa di un'asserzione
Riceviamo un AssertionError che indica che la condizione di asserzione nel test non è riuscita e ci dice che il valore previsto è "Jane Doe" ma il valore effettivo è nullo. Ciò ha senso perché la proprietà "name" non è stata aggiunta alla definizione fittizia della risorsa /content/byline in BylineImplTest.json, quindi aggiungiamola:
-
Aggiorna BylineImplTest.json per definire
"name": "Jane Doe".
code language-json { "byline": { "jcr:primaryType": "nt:unstructured", "sling:resourceType": "wknd/components/content/byline", "name": "Jane Doe" } }
-
Riesegui il test e
testGetName()
ora supera il test.
Verifica di getOccupations() testing-get-occupations
Ok, perfetto! Il primo test è stato superato! Passiamo al test di getOccupations()
. Poiché l'inizializzazione del contesto fittizio è stata eseguita nel metodo @Before setUp()
, questo è disponibile per tutti i metodi @Test
in questo test case, incluso getOccupations()
.
Tenere presente che questo metodo deve restituire un elenco alfabetico ordinato delle occupazioni (decrescenti) memorizzate nella proprietà occupazioni.
-
Aggiorna
testGetOccupations()
come segue: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> expected
definiscono il risultato previsto.ctx.currentResource
imposta la risorsa corrente per la valutazione del contesto sulla definizione di risorsa fittizia in /content/byline. In questo modo BylineImpl.java viene eseguito nel contesto della risorsa fittizia.ctx.request().adaptTo(Byline.class)
crea un'istanza del modello Sling Byline adattandolo dal finto oggetto Request.byline.getOccupations()
richiama il metodo in fase di test,getOccupations()
, sull'oggetto modello Sling Byline.assertEquals(expected, actual)
asserisce che l'elenco previsto è uguale all'elenco effettivo.
-
Ricorda che, proprio come
getName()
in precedenza, BylineImplTest.json non definisce le occupazioni, pertanto questo test non riuscirà se viene eseguito, poichébyline.getOccupations()
restituirà un elenco vuoto.Aggiorna BylineImplTest.json per includere un elenco di occupazioni impostate in ordine non alfabetico per garantire che i test convalidi che le occupazioni siano ordinate alfabeticamente per
getOccupations()
.code language-json { "byline": { "jcr:primaryType": "nt:unstructured", "sling:resourceType": "wknd/components/content/byline", "name": "Jane Doe", "occupations": ["Photographer", "Blogger", "YouTuber"] } }
-
Esegui il test e passiamo di nuovo! Sembra che le occupazioni ordinate funzionino!
testGetOccupations() passa
Test isEmpty() testing-is-empty
Ultimo metodo per testare isEmpty()
.
Il test isEmpty()
è interessante in quanto richiede test per varie condizioni. Esaminando il metodo isEmpty()
di BylineImpl.java, è necessario verificare le seguenti condizioni:
- Restituisce true quando il nome è vuoto
- Restituisce true se le occupazioni sono nulle o vuote
- Restituisce true se l’immagine è null o non ha un URL src
- Restituisce false quando sono presenti il nome, le occupazioni e l’immagine (con un URL src)
A tale scopo, è necessario creare metodi di test, ognuno dei quali esegue il test di una condizione specifica e di nuove strutture di risorse fittizie in BylineImplTest.json
per eseguire questi test.
Questo controllo ci ha permesso di saltare i test per quando getName()
, getOccupations()
e getImage()
sono vuoti, poiché il comportamento previsto di tale stato è testato tramite isEmpty()
.
-
Il primo test verifica la condizione di un componente nuovo, che non ha proprietà impostate.
Aggiungi una nuova definizione di risorsa a
BylineImplTest.json
, assegnandole il nome semantico "empty"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": {...}
definiscono una nuova definizione di risorsa denominata "empty" che ha solojcr:primaryType
esling:resourceType
.Ricorda che abbiamo caricato
BylineImplTest.json
inctx
prima dell'esecuzione di ogni metodo di test in@setUp
, quindi questa nuova definizione di risorsa è immediatamente disponibile nei test in /content/empty. -
Aggiornare
testIsEmpty()
come segue, impostando la risorsa corrente sulla nuova definizione di risorsa fittizia "empty".code language-java @Test public void testIsEmpty() { ctx.currentResource("/content/empty"); Byline byline = ctx.request().adaptTo(Byline.class); assertTrue(byline.isEmpty()); }
Esegui il test e assicurati che venga superato.
-
Creare quindi un set di metodi per garantire che, se uno qualsiasi dei punti di dati richiesti (nome, occupazioni o immagine) è vuoto,
isEmpty()
restituisca true.Per ogni test viene utilizzata una definizione di risorsa fittizia discreta. Aggiornare BylineImplTest.json con le definizioni di risorsa aggiuntive per 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" } }
Crea i seguenti metodi di test per testare ciascuno di questi stati.
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()
verifica la definizione della risorsa fittizia vuota e asserisce cheisEmpty()
è true.testIsEmpty_WithoutName()
esegue il test in base a una definizione di risorsa fittizia con occupazioni ma senza nome.testIsEmpty_WithoutOccupations()
esegue il test in base a una definizione di risorsa fittizia con un nome ma senza occupazioni.testIsEmpty_WithoutImage()
esegue il test in base a una definizione di risorsa fittizia con un nome e occupazioni, ma imposta l'immagine fittizia su null. Si noti che si desidera ignorare il comportamentomodelFactory.getModelFromWrappedRequest(..)
definito insetUp()
per assicurarsi che l'oggetto Image restituito da questa chiamata sia nullo. La funzione Mockito stubs è rigida e non richiede codice duplicato. Pertanto, abbiamo impostato le impostazioni del simulatore conlenient
in modo che notino esplicitamente che stiamo ignorando il comportamento nel metodosetUp()
.testIsEmpty_WithoutImageSrc()
esegue test su una definizione di risorsa fittizia con un nome e occupazioni, ma imposta l'immagine fittizia in modo che restituisca una stringa vuota quando viene richiamatogetSrc()
. -
Infine, scrivere un test per assicurarsi che isEmpty() restituisca false quando il componente è configurato correttamente. Per questa condizione, è possibile riutilizzare /content/byline che rappresenta un componente Byline completamente configurato.
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()); }
-
Ora esegui tutti gli unit test nel file BylineImplTest.java e controlla l’output del rapporto sui test Java™.
Esecuzione degli unit test come parte della build running-unit-tests-as-part-of-the-build
Gli unit test vengono eseguiti e sono necessari per passare come parte della build Maven. In questo modo, tutti i test vengono superati correttamente prima che un’applicazione venga distribuita. L’esecuzione degli obiettivi Maven, ad esempio pacchetto o installazione, richiama automaticamente e richiede il passaggio di tutti gli unit test nel progetto.
$ mvn package
$ mvn package
Allo stesso modo, se si modifica un metodo di test in modo che abbia esito negativo, la build avrà esito negativo e segnalerà quali test non sono riusciti e perché.
Rivedi il codice review-the-code
Visualizza il codice finito in GitHub oppure rivedi e distribuisci il codice localmente in nel ramo Git tutorial/unit-testing-solution
.