Serenity BDD med Spring og JBehave

1. Introduksjon

Tidligere har vi introdusert Serenity BDD-rammeverket.

I denne artikkelen vil vi introdusere hvordan du kan integrere Serenity BDD med våren.

2. Maven avhengighet

For å aktivere Serenity i vårprosjektet, må vi legge til stillhet-kjerne og stillhet-våren til pom.xml:

 net.serenity-bdd serenity-core 1.4.0 test net.serenity-bdd serenity-spring 1.4.0 test 

Vi må også konfigurere serenity-maven-plugin, som er viktig for å generere Serenity-testrapporter:

 net.serenity-bdd.maven.plugins serenity-maven-plugin 1.4.0 stillhet-rapporter etter integrering-test aggregat 

3. Vårintegrasjon

Vårintegrasjonstest må @RunWithSpringJUnit4ClassRunner. Men vi kan ikke bruke testløperen direkte med Serenity, ettersom Serenity-tester må kjøres av SerenityRunner.

For tester med Serenity kan vi bruke SpringIntegrationMethodRule og SpringIntegrationClassRule for å muliggjøre injeksjon.

Vi vil basere testen vår på et enkelt scenario: gitt et tall når vi legger til et annet nummer, og returnerer deretter summen.

3.1. SpringIntegrationMethodRule

SpringIntegrationMethodRule er en MethodRule brukt på testmetodene. Vårkonteksten vil bli bygget før @Før og etter @BeforeClass.

Anta at vi har en egenskap å injisere i bønnene våre:

 4 

La oss nå legge til SpringIntegrationMethodRule for å aktivere verdinjeksjonen i testen vår:

@RunWith (SerenityRunner.class) @ContextConfiguration (locations = "classpath: adder-beans.xml") public class AdderMethodRuleIntegrationTest {@Rule public SpringIntegrationMethodRule springMethodIntegration = new SpringIntegrationMethodRule (); @Steps private AdderSteps adderSteps; @Value ("# {rekvisitter ['adder']}") privat int adder; @Test offentlig ugyldighet givenNumber_whenAdd_thenSummedUp () {adderSteps.givenNumber (); adderSteps.whenAdd (adder); adderSteps.thenSummedUp (); }}

Den støtter også merknadsnivåkommentarer av vårprøve. Hvis noen testmetoder skader testkonteksten, kan vi markere @DirtiesContext på den:

@RunWith (SerenityRunner.class) @FixMethodOrder (MethodSorters.NAME_ASCENDING) @ContextConfiguration (classes = AdderService.class) public class AdderMethodDirtiesContextIntegrationTest {@Steps private AdderServiceSteps adderServiceSteps; @Rule public SpringIntegrationMethodRule springIntegration = ny SpringIntegrationMethodRule (); @DirtiesContext @ Test offentlig ugyldig _0_givenNumber_whenAddAndAccumulate_thenSummedUp () {adderServiceSteps.givenBaseAndAdder (randomInt (), randomInt ()); adderServiceSteps.whenAccumulate (); adderServiceSteps.summedUp (); adderServiceSteps.whenAdd (); adderServiceSteps.sumWrong (); } @Test offentlig ugyldig _1_givenNumber_whenAdd_thenSumWrong () {adderServiceSteps.whenAdd (); adderServiceSteps.sumWrong (); }}

I eksemplet ovenfor, når vi påberoper oss adderServiceSteps.whenAccumulate (), basenummerfeltet til @Service injisert i adderServiceSteps vil bli endret:

@ContextConfiguration (klasser = AdderService.class) offentlig klasse AdderServiceSteps {@Autowired private AdderService adderService; private int givenNumber; privat int base; privat int sum; public void givenBaseAndAdder (int base, int adder) {this.base = base; adderService.baseNum (base); this.givenNumber = adder; } offentlig ugyldig nårAdd () {sum = adderService.add (givenNumber); } public void summedUp () {assertEquals (base + givenNumber, sum); } public void sumWrong () {assertNotEquals (base + givenNumber, sum); } offentlig ugyldig nårAkkumuler () {sum = adderService.accumulate (givenNumber); }}

Spesielt tildeler vi summen til basenummeret:

@Service offentlig klasse AdderService {private int num; public void baseNum (int base) {this.num = base; } public int currentBase () {return num; } public int add (int adder) {return this.num + adder; } public int accumulate (int adder) {return this.num + = adder; }}

I den første testen _0_givenNumber_whenAddAndAccumulate_thenSummedUp, blir grunnnummeret endret, noe som gjør konteksten skitten. Når vi prøver å legge til et annet nummer, får vi ikke en forventet sum.

Legg merke til at selv om vi merket den første testen med @DirtiesContext, den andre testen er fortsatt berørt: etter tilsetning er summen fortsatt feil. Hvorfor?

Nå, mens du behandler metodenivå @DirtiesContext, Serenitys vårintegrasjon gjenoppbygger bare testkonteksten for den nåværende testinstansen. Den underliggende avhengighetskonteksten i @Strinn vil ikke bli gjenoppbygd.

For å omgå dette problemet kan vi injisere @Service i vår nåværende testinstans, og gjør tjenesten som en eksplisitt avhengighet av @Strinn:

@RunWith (SerenityRunner.class) @FixMethodOrder (MethodSorters.NAME_ASCENDING) @ContextConfiguration (classes = AdderService.class) offentlig klasse AdderMethodDirtiesContextDependencyWorkaroundIntegrationTest {private AdderConstructorDependencySteps adderSteps @Autowired privat AdderService adderService; @Før offentlige ugyldig init () {adderSteps = ny AdderConstructorDependencySteps (adderService); } // ...}
offentlig klasse AdderConstructorDependencySteps {private AdderService adderService; offentlige AdderConstructorDependencySteps (AdderService adderService) {this.adderService = adderService; } // ...}

Eller vi kan sette vilkårets initialiseringstrinn i @Før seksjonen for å unngå skitten kontekst. Men denne typen løsning er kanskje ikke tilgjengelig i noen komplekse situasjoner.

@RunWith (SerenityRunner.class) @FixMethodOrder (MethodSorters.NAME_ASCENDING) @ContextConfiguration (classes = AdderService.class) public class AdderMethodDirtiesContextInitWorkaroundIntegrationTest {@Steps private AdderServiceSteps adderServiceSteps @Før offentlig ugyldig init () {adderServiceSteps.givenBaseAndAdder (randomInt (), randomInt ()); } // ...}

3.2. SpringIntegrationClassRule

For å muliggjøre merknader på klassenivå, bør vi bruke SpringIntegrationClassRule. Si at vi har følgende testklasser; hver forringer konteksten:

@RunWith (SerenityRunner.class) @ContextConfiguration (klasser = AdderService.class) offentlig statisk abstrakt klasse Base {@Steps AdderServiceSteps adderServiceSteps; @ClassRule offentlig statisk SpringIntegrationClassRule springIntegrationClassRule = ny SpringIntegrationClassRule (); ugyldig nårAccumulate_thenSummedUp () {adderServiceSteps.whenAccumulate (); adderServiceSteps.summedUp (); } ugyldig nårAdd_thenSumWrong () {adderServiceSteps.whenAdd (); adderServiceSteps.sumWrong (); } ugyldig nårAdd_thenSummedUp () {adderServiceSteps.whenAdd (); adderServiceSteps.summedUp (); }}
@DirtiesContext (classMode = AFTER_CLASS) offentlig statisk klasse DirtiesContextIntegrationTest utvider Base {@Test public void givenNumber_whenAdd_thenSumWrong () {super.whenAdd_thenSummedUp (); adderServiceSteps.givenBaseAndAdder (randomInt (), randomInt ()); super.whenAccumulate_thenSummedUp (); super.whenAdd_thenSumWrong (); }}
@DirtiesContext (classMode = AFTER_CLASS) offentlig statisk klasse AnotherDirtiesContextIntegrationTest utvider Base {@Test public void givenNumber_whenAdd_thenSumWrong () {super.whenAdd_thenSummedUp (); adderServiceSteps.givenBaseAndAdder (randomInt (), randomInt ()); super.whenAccumulate_thenSummedUp (); super.whenAdd_thenSumWrong (); }}

I dette eksemplet vil alle implisitte injeksjoner bli gjenoppbygd for klassenivå @DirtiesContext.

3.3. SpringIntegrationSerenityRunner

Det er en praktisk klasse SpringIntegrationSerenityRunner som automatisk legger til begge integrasjonsreglene ovenfor. Vi kan kjøre tester ovenfor med denne løperen for å unngå å spesifisere metoden eller klassetestreglene i testen vår:

@RunWith (SpringIntegrationSerenityRunner.class) @ContextConfiguration (locations = "classpath: adder-beans.xml") offentlig klasse AdderSpringSerenityRunnerIntegrationTest {@Steps private AdderSteps adderSteps; @Value ("# {rekvisitter ['adder']}") privat int adder; @Test offentlig ugyldig givenNumber_whenAdd_thenSummedUp () {adderSteps.givenNumber (); adderSteps.whenAdd (adder); adderSteps.thenSummedUp (); }}

4. SpringMVC-integrasjon

I tilfeller der vi bare trenger å teste SpringMVC-komponenter med Serenity, kan vi bare bruke RestAssuredMockMvc i trygg i stedet for stillhet-våren integrering.

4.1. Maven avhengighet

Vi må legge til den avhengige vår-mock-mvc-avhengigheten til pom.xml:

 io.rest-forsikret vår-mock-mvc 3.0.3 test 

4.2. RestAssuredMockMvc i aksjon

La oss nå teste følgende kontroller:

@RequestMapping (verdi = "/ adder", produserer = MediaType.APPLICATION_JSON_UTF8_VALUE) @RestController offentlig klasse PlainAdderController {private final int currentNumber = RandomUtils.nextInt (); @GetMapping ("/ current") public int currentNum () {return currentNumber; } @ PostMapping public int add (@RequestParam int num) {return currentNumber + num; }}

Vi kan dra nytte av MVC-mocking-verktøyene til RestAssuredMockMvc som dette:

@RunWith (SerenityRunner.class) offentlig klasse AdderMockMvcIntegrationTest {@Før offentlig ugyldig init () {RestAssuredMockMvc.standaloneSetup (ny PlainAdderController ()); } @Steps AdderRestSteps trinn; @Test offentlig ugyldighet givenNumber_whenAdd_thenSummedUp () kaster unntak {trinn.givenCurrentNumber (); steps.whenAddNumber (randomInt ()); trinn.thenSummedUp (); }}

Da er resten ikke forskjellig fra hvordan vi bruker være trygg:

offentlig klasse AdderRestSteps {private MockMvcResponse mockMvcResponse; private int currentNum; @Step ("få det nåværende nummeret") offentlig ugyldighet givenCurrentNumber () kaster UnsupportedEncodingException {currentNum = Integer.valueOf (given () .when () .get ("/ adder / current") .mvcResult () .getResponse (). getContentAsString ()); } @Step ("legger til {0}") offentlig ugyldig nårAddNumber (int num) {mockMvcResponse = gitt () .queryParam ("num", num) .when () .post ("/ adder"); currentNum + = num; } @Step ("fikk summen") offentlig ugyldig thenSummedUp () {mockMvcResponse .then () .statusCode (200) .body (equalTo (currentNum + "")); }}

5. Serenity, JBehave og Spring

Serenitys vårintegrasjonsstøtte fungerer sømløst med JBehave. La oss skrive testscenariet vårt som en JBehave-historie:

Scenario: En bruker kan sende inn et nummer til huggeren og få summen Gitt et nummer

Vi kan implementere logikkene i en @Service og avsløre handlingene via APIer:

@RequestMapping (verdi = "/ adder", produserer = MediaType.APPLICATION_JSON_UTF8_VALUE) @RestController offentlig klasse AdderController {privat AdderService adderService; offentlig AdderController (AdderService adderService) {this.adderService = adderService; } @GetMapping ("/ current") public int currentNum () {return adderService.currentBase (); } @ PostMapping public int add (@RequestParam int num) {return adderService.add (num); }}

Nå kan vi bygge Serenity-JBehave-testen ved hjelp av RestAssuredMockMvc som følger:

@ContextConfiguration (klasser = {AdderController.class, AdderService.class}) offentlig klasse AdderIntegrationTest utvider SerenityStory {@Autowired private AdderService adderService; @BeforeStory public void init () {RestAssuredMockMvc.standaloneSetup (ny AdderController (adderService)); }}
offentlig klasse AdderStory {@Steps AdderRestSteps restSteps; @Given ("et nummer") offentlig ugyldighet givenANumber () kaster Unntak {restSteps.givenCurrentNumber (); } @When ("Jeg sender inn et annet nummer $ num til adder") offentlig ugyldig nårISubmitToAdderWithNumber (int num) {restSteps.whenAddNumber (num); } @Then ("Jeg får en sum av tallene") offentlig ugyldig thenIGetTheSum () {restSteps.thenSummedUp (); }}

Vi kan bare merke SerenityStory med @ContextConfiguration, så aktiveres vårinjeksjonen automatisk. Dette fungerer ganske likt som @ContextConfiguration@Strinn.

6. Sammendrag

I denne artikkelen tok vi for oss hvordan vi kan integrere Serenity BDD med Spring. Integrasjonen er ikke helt perfekt, men den kommer absolutt dit.

Som alltid kan full implementering bli funnet på GitHub-prosjektet.


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