Serenity BDD med Spring og JBehave

1. Introduktion

Tidligere har vi introduceret Serenity BDD-rammen.

I denne artikel introducerer vi, hvordan du integrerer Serenity BDD med Spring.

2. Maven-afhængighed

For at aktivere Serenity i vores Spring-projekt skal vi tilføje sindsro-kerne og sindsro-foråret til pom.xml:

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

Vi skal også konfigurere serenity-maven-plugin, hvilket er vigtigt for at generere Serenity-testrapporter:

 net.serenity-bdd.maven.plugins serenity-maven-plugin 1.4.0 sindsro-rapporter efter integration-test-aggregat 

3. Forårsintegration

Spring integration test skal @RunWithSpringJUnit4ClassRunner. Men vi kan ikke bruge testkøreren direkte med Serenity, da Serenity-tests skal køres af SerenityRunner.

Til tests med Serenity kan vi bruge SpringIntegrationMethodRule og SpringIntegrationClassRule for at muliggøre injektion.

Vi baserer vores test på et simpelt scenario: givet et tal, når vi tilføjer et andet nummer, returnerer derefter summen.

3.1. SpringIntegrationMethodRule

SpringIntegrationMethodRule er en Metode Regel anvendt på testmetoderne. Forårssammenhængen vil blive bygget før @Før og efter @BeforeClass.

Antag, at vi har en egenskab, der skal injiceres i vores bønner:

 4 

Lad os nu tilføje SpringIntegrationMethodRule for at aktivere værdinjektionen i vores test:

@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 ugyldighed givenNumber_whenAdd_thenSummedUp () {adderSteps.givenNumber (); adderSteps.whenAdd (adder); adderSteps.thenSummedUp (); }}

Den understøtter også annoteringer på metodeniveau forårstest. Hvis en eller anden testmetode beskadiger testkonteksten, kan vi markere @DirtiesContext på det:

@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 ugyldigt _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åberåber os adderServiceSteps.whenAccumulate (), basisnummerfeltet for @Service injiceret i adderServiceSteps vil blive ændret:

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

Specifikt tildeler vi summen til basisnummeret:

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

I den første test _0_givenNumber_whenAddAndAccumulate_thenSummedUp, ændres basisnummeret, hvilket gør konteksten beskidt. Når vi forsøger at tilføje et andet nummer, får vi ikke et forventet beløb.

Bemærk, at selvom vi markerede den første test med @DirtiesContext, den anden test er stadig påvirket: efter tilføjelse er summen stadig forkert. Hvorfor?

Nu, mens du behandler metodeniveau @DirtiesContext, Serenity's Spring-integration genopbygger kun testkonteksten for den aktuelle testinstans. Den underliggende afhængighedskontekst i @ Trin vil ikke blive genopbygget.

For at omgå dette problem kan vi indsprøjte @Service i vores nuværende testinstans, og lav service som en eksplicit afhængighed af @ Trin:

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

Eller vi kan sætte betingelsen initialiseringstrin i @Før sektion for at undgå beskidt kontekst. Men denne form for løsning er muligvis ikke tilgængelig i nogle komplekse situationer.

@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 at aktivere klasseniveaukommentarer skal vi bruge SpringIntegrationClassRule. Sig, at vi har følgende testklasser; hver ødelægger sammenhængen:

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

I dette eksempel vil alle implicitte injektioner blive genopbygget til klasseniveau @DirtiesContext.

3.3. SpringIntegrationSerenityRunner

Der er en bekvem klasse SpringIntegrationSerenityRunner der tilføjer automatisk begge integrationsregler ovenfor. Vi kan køre tests ovenfor med denne løber for at undgå at specificere metoden eller klassetestreglerne i vores test:

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

4. SpringMVC-integration

I tilfælde hvor vi kun har brug for at teste SpringMVC-komponenter med Serenity, kan vi simpelthen gøre brug af RestAssuredMockMvc i rolig i stedet for sindsro-foråret integration.

4.1. Maven afhængighed

Vi er nødt til at tilføje den forsikrede fjeder-mock-mvc-afhængighed til pom.xml:

 io.rest-forsikret fjeder-mock-mvc 3.0.3 test 

4.2. RestAssuredMockMvc i aktion

Lad os nu teste følgende controller:

@RequestMapping (værdi = "/ adder", producerer = 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 drage fordel af MVC-mocking-værktøjer fra RestAssuredMockMvc sådan her:

@RunWith (SerenityRunner.class) offentlig klasse AdderMockMvcIntegrationTest {@Før offentlig ugyldig init () {RestAssuredMockMvc.standaloneSetup (ny PlainAdderController ()); } @Steps trin i AdderRestSteps; @Test offentlig ugyldighed givenNumber_whenAdd_thenSummedUp () kaster undtagelse {trin.givenCurrentNumber (); step.whenAddNumber (randomInt ()); step.thenSummedUp (); }}

Så er resten ikke forskellig fra, hvordan vi bruger stol trygt på:

offentlig klasse AdderRestSteps {private MockMvcResponse mockMvcResponse; privat int nuværendeNum; @Step ("get the current number") public void givenCurrentNumber () throw UnsupportedEncodingException {currentNum = Integer.valueOf (given () .when () .get ("/ adder / current") .mvcResult () .getResponse (). getContentAsString ()); } @Step ("tilføjer {0}") offentligt ugyldigt nårAddNumber (int num) {mockMvcResponse = given () .queryParam ("num", num) .when () .post ("/ adder"); nuværendeNum + = num; } @Step ("fik summen") offentlig ugyldighed thenSummedUp () {mockMvcResponse .then () .statusCode (200) .body (equalTo (currentNum + "")); }}

5. Serenity, JBehave og Spring

Serenitys forårsintegrationssupport fungerer problemfrit med JBehave. Lad os skrive vores testscenarie som en JBehave-historie:

Scenarie: En bruger kan sende et nummer til addereren og få summen givet et nummer Når jeg sender et andet nummer 5 til addereren Så får jeg en sum af tallene

Vi kan implementere logikken i en @Service og udsætte handlingerne via API'er:

@RequestMapping (værdi = "/ adder", producerer = 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); }}

Nu kan vi opbygge Serenity-JBehave-test ved hjælp af RestAssuredMockMvc som følger:

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

Vi kan kun markere SerenityStory med @ContextConfiguration, så aktiveres fjederindsprøjtning automatisk. Dette fungerer helt det samme som @ContextConfiguration@ Trin.

6. Resume

I denne artikel behandlede vi, hvordan du integrerer Serenity BDD med Spring. Integrationen er ikke helt perfekt, men den kommer helt sikkert dertil.

Som altid kan den fulde implementering findes på GitHub-projektet.


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