Skriveskabeloner til testsager ved hjælp af JUnit 5

1. Oversigt

JUnit 5-biblioteket tilbyder mange nye funktioner i forhold til dets tidligere versioner. En sådan funktion er testskabeloner. Kort fortalt er testskabeloner en stærk generalisering af JUnit 5s parametriserede og gentagne test.

I denne vejledning lærer vi, hvordan du opretter en testskabelon ved hjælp af JUnit 5.

2. Maven-afhængigheder

Lad os starte med at tilføje afhængighederne til vores pom.xml.

Vi er nødt til at tilføje de vigtigste JUnit 5 junit-jupiter-motor afhængighed:

 org.junit.jupiter junit-jupiter-engine 5.7.0 

Ud over dette skal vi også tilføje junit-jupiter-api afhængighed:

 org.junit.jupiter junit-jupiter-api 5.7.0 

På samme måde kan vi tilføje de nødvendige afhængigheder til vores build.gradle fil:

testCompile gruppe: 'org.junit.jupiter', navn: 'junit-jupiter-engine', version: '5.7.0' testCompile gruppe: 'org.junit.jupiter', navn: 'junit-jupiter-api', version : '5.7.0'

3. Problemerklæringen

Før vi kigger på testskabeloner, skal vi kort se på JUnit 5s parametriserede test. Parameteriserede tests giver os mulighed for at indsprøjte forskellige parametre i testmetoden. Som et resultat, hvornår ved hjælp af parametriserede testsvi kan udføre en enkelt testmetode flere gange med forskellige parametre.

Lad os antage, at vi nu gerne vil køre vores testmetode flere gange - ikke kun med forskellige parametre, men også under en anden indkaldelseskontekst hver gang.

Med andre ord, Vi vil gerne have, at testmetoden udføres flere gange med hver påkaldelse ved hjælp af en anden kombination af konfigurationer såsom:

  • ved hjælp af forskellige parametre
  • forberede testklasseinstansen forskelligt - dvs. injicere forskellige afhængigheder i testinstansen
  • kører testen under forskellige forhold, såsom aktivering / deaktivering af en undersæt af påkald, hvis miljøet er “QA
  • kører med en anden livscyklus tilbagekaldsadfærd - måske vil vi oprette og nedbryde en database før og efter et undersæt af påkald

Brug af parametriserede tests viser sig hurtigt at være begrænset i dette tilfælde. Heldigvis tilbyder JUnit 5 en effektiv løsning til dette scenarie i form af testskabeloner.

4. Test skabeloner

Selve testskabeloner er ikke testtilfælde. I stedet for, som deres navn antyder, er de bare skabeloner til givne testsager. De er en stærk generalisering af parametriserede og gentagne tests.

Testskabeloner påkaldes én gang for hver indkaldelseskontekst, der leveres til dem af indkaldelseskontekstudbyderen / -erne.

Lad os nu se på et eksempel på testskabeloner. Som vi har fastslået ovenfor, er hovedaktørerne:

  • en testmålmetode
  • en testskabelonmetode
  • en eller flere udbydere kontekstudbydere registreret med skabelonmetoden
  • en eller flere påkaldelseskontekster leveret af hver udbyders kontekstudbyder

4.1. Testmålmetoden

I dette eksempel skal vi bruge en simpel UserIdGeneratorImpl.genereret metode som vores testmål.

Lad os definere UserIdGeneratorImpl klasse:

offentlig klasse UserIdGeneratorImpl implementerer UserIdGenerator {privat boolsk isFeatureEnabled; offentlig UserIdGeneratorImpl (boolsk isFeatureEnabled) {this.isFeatureEnabled = isFeatureEnabled; } offentlig streng genererer (streng fornavn, streng efternavn) {streng initialAndLastName = fornavn.substring (0, 1) .concat (efternavn); return isFeatureEnabled? "bael" .concat (initialAndLastName): initialAndLastName; }}

Det frembringe metoden, som er vores testmål, tager fornavn og efternavn som parametre og genererer et bruger-id. Bruger-id'ets format varierer afhængigt af om en funktionsomskifter er aktiveret eller ej.

Lad os se, hvordan dette ser ud:

Givet funktionsomskifter er deaktiveret Når fornavn = "John" og efternavn = "Smith" Så returneres "JSmith" givet funktionsomskifter er aktiveret Når fornavn = "John" og efternavn = "Smith" Derefter returneres "baelJSmith"

Lad os derefter skrive testskabelonmetoden.

4.2. Testskabelonmetoden

Her er en testskabelon til vores testmålmetode UserIdGeneratorImpl.genereret:

public class UserIdGeneratorImplUnitTest {@TestTemplate @ExtendWith (UserIdGeneratorTestInvocationContextProvider.class) public void whenUserIdRequested_thenUserIdIsReturnedInCorrectFormat (UserIdGeneratorTestCase testGenerator = UserGenerator) UserIdentatorGenerator (UserIdGeneratorTestCase testGenerator) {UserIdGenerator = UserGenerator) Streng actualUserId = userIdGenerator.generate (testCase.getFirstName (), testCase.getLastName ()); assertThat (actualUserId) .isEqualTo (testCase.getExpectedUserId ()); }}

Lad os se nærmere på testskabelonmetoden.

Til at starte med, vi opretter vores testskabelonmetode ved at markere den med JUnit 5 @TestTemplate kommentar.

Efter det, vi registrerer en kontekstudbyder, UserIdGeneratorTestInvocationContextProvider,bruger @ExtendWith kommentar. Vi kan registrere flere kontekstudbydere med testskabelonen. I forbindelse med dette eksempel registrerer vi dog en enkelt udbyder.

Skabelonmetoden modtager også en forekomst af UserIdGeneratorTestCase som en parameter. Dette er simpelthen en indpakningsklasse for input og det forventede resultat af testsagen:

offentlig klasse UserIdGeneratorTestCase {privat boolsk isFeatureEnabled; privat streng fornavn; privat streng efternavn; private String expectUserId; // Standard settere og getters}

Endelig påkalder vi testmålmetoden og hævder, at resultatet er som forventet

Nu er det tid til at definere vores udbydere.

4.3. Invocation Context Provider

Vi skal registrere mindst en TestTemplateInvocationContextProvider med vores testskabelon. Hver registreret TestTemplateInvocationContextProvider giver en Strøm af TestTemplateInvocationContext tilfælde.

Tidligere ved hjælp af @ExtendWith kommentar, vi registrerede UserIdGeneratorTestInvocationContextProvider som vores udbydere.

Lad os definere denne klasse nu:

offentlig klasse UserIdGeneratorTestInvocationContextProvider implementerer TestTemplateInvocationContextProvider {// ...}

Vores indkaldelseskontekst implementerer TestTemplateInvocationContextProvider interface, som har to metoder:

  • understøtter TestTemplate
  • giveTestTemplateInvocationContexts

Lad os starte med at implementere understøtter TestTemplate metode:

@Override public boolean supportsTestTemplate (ExtensionContext extensionContext) {return true; }

JUnit 5-udførelsesmotoren kalder understøtter TestTemplate metode først for at validere, hvis udbyderen gælder for det givne ExecutionContext. I dette tilfælde vender vi simpelthen tilbage rigtigt.

Lad os nu implementere giveTestTemplateInvocationContexts metode:

@Override public Stream giveTestTemplateInvocationContexts (ExtensionContext extensionContext) {boolean featureDisabled = false; boolsk featureEnabled = sand; returner Stream.of (featureDisabledContext (ny UserIdGeneratorTestCase ("Givet funktionsomskifter deaktiveret Når brugernavn er John Smith Derefter genereres userid JSmith", featureDisabled, "John", "Smith", "JSmith")), featureEnabledContext (ny UserIdGeneratorTestCase (" Givet funktionsomskifter aktiveret Når brugernavn er John Smith Så er genereret userid baelJSmith ", featureEnabled," John "," Smith "," baelJSmith "))); }

Formålet med giveTestTemplateInvocationContexts metoden er at tilvejebringe en Strøm af TestTemplateInvocationContext tilfælde. For vores eksempel returnerer det to forekomster leveret af metoderne featureDisabledContext og featureEnabledContext. Derfor kører vores testskabelon to gange.

Lad os derefter se på de to TestTemplateInvocationContext forekomster, der returneres ved hjælp af disse metoder.

4.4. Invokationskontekstforekomsterne

Indkaldelseskonteksterne er implementeringer af TestTemplateInvocationContext interface og implementere følgende metoder:

  • getDisplayName - angiv et testvisningsnavn
  • getAdditionalExtensions - returnere yderligere udvidelser til indkaldelseskonteksten

Lad os definere featureDisabledContext metode, der returnerer vores første indkaldelseskontekstforekomst:

privat TestTemplateInvocationContext featureDisabledContext (UserIdGeneratorTestCase userIdGeneratorTestCase) {returner ny TestTemplateInvocationContext () {@ Override public String getDisplayName (int invocationIndex) {return userIdGeneratorTestCase.getDisplayName (); } @Override public List getAdditionalExtensions () {return asList (new GenericTypedParameterResolver (userIdGeneratorTestCase), new BeforeTestExecutionCallback () {@Override public void beforeTestExecution (ExtensionContext extensionContext) {System.out.printlecution ("Before: Test) ny AfterTestExecutionCallback () {@Override offentlig ugyldighed afterTestExecution (ExtensionContext extensionContext) {System.out.println ("AfterTestExecutionCallback: Disabled context");}}); }}; }

For det første for påkaldelseskonteksten returneret af featureDisabledContext metode, er de udvidelser, vi registrerer:

  • GenericTypedParameterResolver - en parameter resolver udvidelse
  • BeforeTestExecutionCallback - en tilbagekaldelsesudvidelse til livscyklus, der kører umiddelbart før testudførelsen
  • AfterTestExecutionCallback - en livscyklus-tilbagekaldsudvidelse, der kører umiddelbart efter testudførelsen

Dog for den anden påkaldelseskontekst, returneret af featureEnabledContext metode, lad os registrere et andet sæt udvidelser (holde GenericTypedParameterResolver):

privat TestTemplateInvocationContext featureEnabledContext (UserIdGeneratorTestCase userIdGeneratorTestCase) {return new TestTemplateInvocationContext () {@Override public String getDisplayName (int invocationIndex) {return userIdGeneratorTestCase.getDisplayName (); } @Override public List getAdditionalExtensions () {return asList (new GenericTypedParameterResolver (userIdGeneratorTestCase), new DisabledOnQAEnvironmentExtension (), new BeforeEachCallback () {@ Override public void beforeEach (ExtensionContext extensionContext): Context.Context. );}}, ny AfterEachCallback () {@Override offentlig ugyldig efterEach (ExtensionContext extensionContext) {System.out.println ("AfterEachCallback: Enabled context");}}); }}; }

For den anden indkaldelseskontekst er de udvidelser, vi registrerer:

  • GenericTypedParameterResolver - en parameter resolver udvidelse
  • DisabledOnQAEnvironmentExtension - en eksekveringsbetingelse for at deaktivere testen, hvis miljøegenskaben (indlæst fra application.properties fil) er “qa
  • BeforeEachCallback - en tilbagekaldelsesudvidelse til livscyklus, der kører før hver testmetodeudførelse
  • AfterEachCallback - en tilbagekaldelsesudvidelse til livscyklus, der kører efter hver testmetodeudførelse

Fra ovenstående eksempel er det klart at se, at:

  • den samme testmetode køres under flere opkaldssammenhænge
  • hver indkaldelseskontekst bruger sit eget sæt udvidelser, der adskiller sig både i antal og karakter fra udvidelser i andre påkaldelsessammenhænge

Som et resultat kan en testmetode påberåbes flere gange under en helt anden indkaldelseskontekst hver gang. Og ved at registrere flere kontekstudbydere kan vi tilbyde endnu flere ekstra påkaldelseskontekster, hvor testen skal køres.

5. Konklusion

I denne artikel så vi på, hvordan JUnit 5s testskabeloner er en kraftig generalisering af parametriserede og gentagne tests.

Til at begynde med så vi på nogle begrænsninger af de parametriserede tests. Dernæst diskuterede vi, hvordan testskabeloner overvinder begrænsningerne ved at lade en test køre under en anden sammenhæng for hver påkaldelse.

Endelig så vi på et eksempel på oprettelse af en ny testskabelon. Vi delte eksemplet ned for at forstå, hvordan skabeloner fungerer sammen med udbydere og udbydelseskontekster.

Som altid er kildekoden til eksemplerne i denne artikel tilgængelig på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found