Sjablonen schrijven voor testcases met JUnit 5

1. Overzicht

De JUnit 5-bibliotheek biedt veel nieuwe functies ten opzichte van de vorige versies. Een van die functies zijn testsjablonen. Kortom, testsjablonen zijn een krachtige generalisatie van de geparametriseerde en herhaalde tests van JUnit 5.

In deze zelfstudie leren we hoe u een testsjabloon kunt maken met JUnit 5.

2. Maven afhankelijkheden

Laten we beginnen met het toevoegen van de afhankelijkheden aan onze pom.xml.

We moeten de hoofd-JUnit 5 toevoegen junit-jupiter-engine afhankelijkheid:

 org.junit.jupiter junit-jupiter-engine 5.7.0 

Daarnaast moeten we ook het junit-jupiter-api afhankelijkheid:

 org.junit.jupiter junit-jupiter-api 5.7.0 

Evenzo kunnen we de nodige afhankelijkheden toevoegen aan onze build.gradle het dossier:

testCompile groep: 'org.junit.jupiter', naam: 'junit-jupiter-engine', versie: '5.7.0' testCompile groep: 'org.junit.jupiter', naam: 'junit-jupiter-api', versie : '5.7.0'

3. De probleemstelling

Laten we, voordat we naar testsjablonen kijken, kort de geparametriseerde tests van JUnit 5 bekijken. Geparametriseerde tests stellen ons in staat om verschillende parameters in de testmethode te injecteren. Als gevolg hiervan, wanneer met behulp van geparametriseerde tests,we kunnen een enkele testmethode meerdere keren uitvoeren met verschillende parameters.

Laten we aannemen dat we onze testmethode nu meerdere keren willen uitvoeren - niet alleen met verschillende parameters, maar ook elke keer onder een andere aanroepcontext.

Met andere woorden, we willen dat de testmethode meerdere keren wordt uitgevoerd, waarbij elke aanroep een andere combinatie van configuraties gebruikt zoals:

  • met behulp van verschillende parameters
  • de testklasse-instantie anders voorbereiden - dat wil zeggen, verschillende afhankelijkheden in de testinstantie injecteren
  • het uitvoeren van de test onder verschillende omstandigheden, zoals het in- / uitschakelen van een subset van aanroepen als de omgeving is "QA
  • wordt uitgevoerd met een ander terugbelgedrag tijdens de levenscyclus - misschien willen we een database opzetten en afbreken voor en na een subset van aanroepen

Het gebruik van geparametriseerde tests blijkt in dit geval al snel beperkt. Gelukkig biedt JUnit 5 een krachtige oplossing voor dit scenario in de vorm van testsjablonen.

4. Testsjablonen

Testsjablonen zijn zelf geen testcases. In plaats daarvan zijn het, zoals hun naam al doet vermoeden, slechts sjablonen voor bepaalde testgevallen. Ze zijn een krachtige generalisatie van geparametriseerde en herhaalde tests.

Testsjablonen worden eenmaal aangeroepen voor elke aanroepcontext die aan hen wordt verstrekt door de aanroepcontextaanbieder (s).

Laten we nu eens kijken naar een voorbeeld van de testsjablonen. Zoals we hierboven hebben vastgesteld, zijn de belangrijkste actoren:

  • een testdoelmethode
  • een testsjabloonmethode
  • een of meer aanbieders van aanroepcontext die zijn geregistreerd met de sjabloonmethode
  • een of meer aanroepcontexten die door elke aanroepcontextprovider worden geleverd

4.1. De testdoelmethode

Voor dit voorbeeld gebruiken we een simple UserIdGeneratorImpl. Genereren methode als ons testdoel.

Laten we de UserIdGeneratorImpl klasse:

openbare klasse UserIdGeneratorImpl implementeert UserIdGenerator {privé boolean isFeatureEnabled; openbare UserIdGeneratorImpl (boolean isFeatureEnabled) {this.isFeatureEnabled = isFeatureEnabled; } public String genereren (String firstName, String lastName) {String initialAndLastName = firstName.substring (0, 1) .concat (lastName); terug isFeatureEnabled? "bael" .concat (initialAndLastName): initialAndLastName; }}

De genereren methode, die ons testdoel is, neemt de Voornaam en achternaam als parameters en genereert een gebruikers-ID. Het formaat van de gebruikers-ID varieert, afhankelijk van of een functieschakelaar is ingeschakeld of niet.

Laten we eens kijken hoe dit eruit ziet:

Gegeven feature switch is uitgeschakeld Wanneer firstName = "John" en lastName = "Smith" Dan wordt "JSmith" geretourneerd Gegeven feature switch is ingeschakeld Wanneer firstName = "John" en lastName = "Smith" Dan wordt "baelJSmith" geretourneerd

Laten we vervolgens de testsjabloonmethode schrijven.

4.2. De testsjabloonmethode

Hier is een testsjabloon voor onze testdoelmethode UserIdGeneratorImpl. Genereren:

openbare klasse UserIdGeneratorImplUnitTest {@TestTemplate @ExtendWith (UserIdGeneratorTestInvocationContextProvider.class) openbare leegte whenUserIdRequested_thenUserIdIsReturnedInCorrectFormat (UserIdGeneratorTestInvocationContextProvider.class) openbare leegte whenUserIdRequested_thenUserIdIsReturnedInCorrectFormat (UserIdGeneratorTestCase testCase testCase) {UserIdGeneratorTestCase testCase (testCase) String actualUserId = userIdGenerator.generate (testCase.getFirstName (), testCase.getLastName ()); assertThat (actualUserId) .isEqualTo (testCase.getExpectedUserId ()); }}

Laten we de methode van de testsjabloon eens nader bekijken.

Beginnen met, we maken onze testsjabloonmethode door deze te markeren met de JUnit 5 @TestTemplate annotatie.

Daarna we registreren een contextprovider, UserIdGeneratorTestInvocationContextProvider,de ... gebruiken @ExtendWith annotatie. We kunnen meerdere contextproviders registreren met de testsjabloon. Voor dit voorbeeld registreren we echter één provider.

De sjabloonmethode ontvangt ook een exemplaar van de UserIdGeneratorTestCase als parameter. Dit is gewoon een wrapper-klasse voor de invoer en het verwachte resultaat van de testcase:

openbare klasse UserIdGeneratorTestCase {privé boolean isFeatureEnabled; private String voornaam; private String achternaam; private String verwachteUserId; // Standaard setters en getters}

Ten slotte doen we een beroep op de testdoelmethode en stellen we dat dat resultaat is zoals verwacht

Nu is het tijd om onze aanroepcontextprovider te definiëren.

4.3. De aanroepcontextprovider

We moeten er minimaal één registreren TestTemplateInvocationContextProvider met onze testsjabloon. Elk geregistreerd TestTemplateInvocationContextProvider verschaft een Stroom van TestTemplateInvocationContext gevallen.

Eerder gebruikte u de @ExtendWith annotatie, we hebben geregistreerd UserIdGeneratorTestInvocationContextProvider als onze aanroepprovider.

Laten we deze klasse nu definiëren:

openbare klasse UserIdGeneratorTestInvocationContextProvider implementeert TestTemplateInvocationContextProvider {// ...}

Onze aanroepcontext implementeert de TestTemplateInvocationContextProvider interface, die twee methoden heeft:

  • supportsTestTemplate
  • offerTestTemplateInvocationContexts

Laten we beginnen met het implementeren van het supportsTestTemplate methode:

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

De JUnit 5-uitvoeringsengine roept het supportsTestTemplate methode om eerst te valideren of de provider van toepassing is op het gegeven ExecutionContext. In dit geval komen we gewoon terug waar.

Laten we nu het offerTestTemplateInvocationContexts methode:

@Override openbare stream biedenTestTemplateInvocationContexts (ExtensionContext extensionContext) {boolean featureDisabled = false; boolean featureEnabled = true; return Stream.of (featureDisabledContext (nieuwe UserIdGeneratorTestCase ("Gegeven feature switch uitgeschakeld Wanneer gebruikersnaam John Smith is Dan gegenereerde userid is JSmith", featureDisabled, "John", "Smith", "JSmith")), featureEnabledContext (nieuwe UserIdGeneratorTestCase (" Gegeven feature switch ingeschakeld Wanneer gebruikersnaam John Smith is. De gegenereerde userid is baelJSmith ", featureEnabled," John "," Smith "," baelJSmith "))); }

Het doel van de offerTestTemplateInvocationContexts methode is om een Stroom van TestTemplateInvocationContext gevallen. Voor ons voorbeeld retourneert het twee instanties, geleverd door de methoden featureDisabledContext en featureEnabledContext. Bijgevolg zal onze testsjabloon twee keer worden uitgevoerd.

Laten we vervolgens naar de twee kijken TestTemplateInvocationContext instanties die door deze methoden worden geretourneerd.

4.4. De Invocation Context Instances

De aanroepcontexten zijn implementaties van het TestTemplateInvocationContext interface en implementeer de volgende methoden:

  • getDisplayName - geef een testweergavenaam op
  • getAdditionalExtensions - retourneer extra extensies voor de aanroepcontext

Laten we de featureDisabledContext methode die onze eerste aanroepcontextinstantie retourneert:

private TestTemplateInvocationContext featureDisabledContext (UserIdGeneratorTestCase userIdGeneratorTestCase) {return new TestTemplateInvocationContext () {@Override public String getDisplayName (int invocationIndex) {return userIdGeneratorTestCase.getDisplayName (); } @Override openbare lijst getAdditionalExtensions () {return asList (nieuwe GenericTypedParameterResolver (userIdGeneratorTestCase), nieuwe BeforeTestExecutionCallback () {@Override public void beforeTestExecution (ExtensionContext extensionContext) {System.outCallbackln:} nieuwe AfterTestExecutionCallback () {@Override public void afterTestExecution (ExtensionContext extensionContext) {System.out.println ("AfterTestExecutionCallback: Disabled context");}}); }}; }

Ten eerste voor de aanroepcontext die wordt geretourneerd door de featureDisabledContext methode, de extensies die we registreren zijn:

  • GenericTypedParameterResolver - een extensie voor parameterresolver
  • BeforeTestExecutionCallback - een callback-extensie voor de levenscyclus die onmiddellijk voor de uitvoering van de test wordt uitgevoerd
  • AfterTestExecutionCallback - een callback-extensie voor de levenscyclus die onmiddellijk na de testuitvoering wordt uitgevoerd

Voor de tweede aanroepcontext, geretourneerd door de featureEnabledContext methode, laten we een andere set extensies registreren (met behoud van de GenericTypedParameterResolver):

private TestTemplateInvocationContext featureEnabledContext (UserIdGeneratorTestCase userIdGeneratorTestCase) {return new TestTemplateInvocationContext () {@Override public String getDisplayName (int invocationIndex) {return userIdGeneratorTestCase.getDisplayName (); } @Override openbare lijst getAdditionalExtensions () {return asList (nieuwe GenericTypedParameterResolver (userIdGeneratorTestCase), nieuwe DisabledOnQAEnvironmentExtension (), nieuwe BeforeEachCallback () {@Override public void beforeEach (ExtensionContext "extension": Enige context "-extensie": ": Enige context". );}}, nieuwe AfterEachCallback () {@Override public void afterEach (ExtensionContext extensionContext) {System.out.println ("AfterEachCallback: Enabled context");}}); }}; }

Voor de tweede aanroepcontext zijn de extensies die we registreren:

  • GenericTypedParameterResolver - een extensie voor parameterresolver
  • DisabledOnQAEnvironmentExtension - een uitvoeringsvoorwaarde om de test uit te schakelen als de omgevingseigenschap (geladen vanuit het application.properties bestand) is “qa
  • BeforeEachCallback - een callback-extensie voor de levenscyclus die wordt uitgevoerd voordat elke testmethode wordt uitgevoerd
  • AfterEachCallback - een callback-extensie voor de levenscyclus die wordt uitgevoerd na elke uitvoering van de testmethode

Uit het bovenstaande voorbeeld blijkt duidelijk dat:

  • dezelfde testmethode wordt uitgevoerd onder meerdere aanroepcontexten
  • elke aanroepcontext gebruikt zijn eigen set extensies die zowel in aantal als aard verschillen van de extensies in andere aanroepcontexten

Het resultaat is dat een testmethode meerdere keren kan worden aangeroepen onder een volledig andere aanroepcontext. En door meerdere contextproviders te registreren, kunnen we nog meer extra lagen aanroepcontexten bieden waaronder we de test kunnen uitvoeren.

5. Conclusie

In dit artikel hebben we gekeken naar hoe de testsjablonen van JUnit 5 een krachtige generalisatie zijn van geparametriseerde en herhaalde tests.

Om te beginnen hebben we gekeken naar enkele beperkingen van de geparametriseerde tests. Vervolgens hebben we besproken hoe testsjablonen de beperkingen overwinnen door voor elke aanroep een test onder een andere context te laten draaien.

Ten slotte hebben we gekeken naar een voorbeeld van het maken van een nieuw testsjabloon. We hebben het voorbeeld opgesplitst om te begrijpen hoe sjablonen werken in combinatie met de aanroepcontextaanbieders en aanroepcontexten.

Zoals altijd is de broncode voor de voorbeelden die in dit artikel worden gebruikt, beschikbaar op GitHub.