Behavior driven integration with Cucumber & Citrus

34
#DevoxxUS Behavior driven integration with Cucumber & Citrus Christoph Deppisch ConSol Software GmbH @cucumberbdd @citrus_test

Transcript of Behavior driven integration with Cucumber & Citrus

Page 1: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Behavior driven integration

with Cucumber & Citrus

Christoph Deppisch ConSol Software GmbH

@cucumberbdd @citrus_test

Page 2: Behavior driven integration with Cucumber & Citrus

#DevoxxUS @citrus_test

http://cucumber.io

http://citrusframework.org

@cucumberbdd

Page 3: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Behavior Driven Development

@citrus_test@cucumberbdd

Page 4: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Communication

@citrus_test@cucumberbdd

Page 5: Behavior driven integration with Cucumber & Citrus

#DevoxxUS @citrus_test

=

@cucumberbdd

Page 6: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Explaining the behavior

@citrus_test@cucumberbdd

Page 7: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Concrete Examples

@citrus_test@cucumberbdd

Page 8: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Given a certain context When some event happens

Then an outcome should occur

@citrus_test

Gherkin

@cucumberbdd

Page 9: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Voting application

@citrus_test@cucumberbdd

Page 10: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Feature specificationFeature: Create voting As a user I want to create new votings. Each voting is given default vote options. Scenario: Default voting options Given voting title "Do you like Mondays?" When I create new voting Then voting should have 2 options And voting should have option "yes" And voting should have option „no" And voting title should be "Do you like Mondays?"

@citrus_test@cucumberbdd

Page 11: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Feature specification continuedFeature: Create voting As a user I want to create new votings. Each voting is given default vote options. The user should be able to set custom vote options. Scenario: Default voting options Given voting title "Do you like Mondays?" When I create new voting Then voting should have 2 options And voting should have option "yes" And voting should have option „no" And voting title should be "Do you like Mondays?" Scenario: Custom voting options When I create new voting "What is your favorite color?" And voting options are "green:red:blue" Then voting title should be "What is your favorite color?" And voting should have options | green | | red | | blue |

@citrus_test@cucumberbdd

Page 12: Behavior driven integration with Cucumber & Citrus

#DevoxxUS @citrus_test@cucumberbdd

Page 13: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

JUnit Cucumber test@RunWith(Cucumber.class) public class VotingFeatureTest {}

@citrus_test

voting-close.feature voting-create.feature voting-results.feature

public class VotingSteps { @Given("^New default voting$") public void defaultVoting() { ... } @When("^I create new voting \"(.+)\"$") public void createVotingWithTitle(String title) { ... } @Then("^voting should have (\\d+) options$") public void votingShouldHaveOptions(int optionCount) { ... } }

@cucumberbdd

Page 14: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Step definitions

@When("^I create new voting \"(.+)\"$") public void createVotingWithTitle(String title) { votingId = UUID.randomUUID(); Voting voting = new Voting(votingId, title); votingService.add(voting);}

@citrus_test

@Then("^voting should have (\\d+) options$") public void votingShouldHaveOptions(int optionCount) { Assert.assertEquals(optionCount, votingService.get(votingId).getOptions().size());}

When I create new voting "What is your favorite color?"

Then voting should have 3 options

@cucumberbdd

Page 15: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Data tables

@Then("^(?:the )?voting should have options$") public void votingShouldHaveOptions(DataTable dataTable) { List<String> options = dataTable.asList(String.class); votingShouldHaveOptions(options.size()); for(String option : options) { votingShouldHaveOption(option); }}

@citrus_test

Then voting should have options| green | | red | | blue |

@cucumberbdd

Page 16: Behavior driven integration with Cucumber & Citrus

#DevoxxUS @citrus_test

Demo

@cucumberbdd

Page 17: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Hooks@Beforepublic void before(Scenario scenario) { // do something} @Afterpublic void after(Scenario scenario) { // do something}

@citrus_test@cucumberbdd

Page 18: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

BackgroundFeature: Show voting results As a user I want to vote for an option. All voting results are stored and the user should be able to get top vote option for each voting. Background: Given I create new voting "Do you like cucumbers?" And voting options are "yes:no" Scenario: Initial vote results Then votes of option "yes" should be 0 And votes of option "no" should be 0 Scenario: Get vote results When I vote for "yes" Then votes of option "yes" should be 1 And votes of option "no" should be 0 And top vote should be "yes"

@citrus_test@cucumberbdd

Page 19: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Scenario OutlineFeature: Show voting results As a user I want to vote for an option. All voting results are stored and the user should be able to get top vote option for each voting. Scenario Outline: Get vote results Given I create new voting "<title>" And voting options are "yes:no" When I vote for "yes" <yes_votes> times And I vote for "no" <no_votes> times Then votes of option "yes" should be <yes_votes> And votes of option "no" should be <no_votes> And top vote should be "<top_vote>"

Examples: | title | yes_votes | no_votes | top_vote | | Do you like hotdogs? | 12 | 5 | yes | | Do you like crap sandwiches? | 1 | 25 | no |

@citrus_test@cucumberbdd

Page 20: Behavior driven integration with Cucumber & Citrus

#DevoxxUS @citrus_test@cucumberbdd

Page 21: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Messaging Integration

@citrus_test@cucumberbdd

Page 22: Behavior driven integration with Cucumber & Citrus

#DevoxxUS @citrus_test

Client VotingAppHttp REST

JMS

Reporting

MailServer

JMS

SMTP

@cucumberbdd

Page 23: Behavior driven integration with Cucumber & Citrus

#DevoxxUS @citrus_test

VotingAppHttp REST

JMS

JMS

SMTP

@cucumberbdd

Page 24: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Citrus Object Factory

cucumber.api.java.ObjectFactory=cucumber.runtime.java.CitrusObjectFactory

@citrus_test

cucumber.properties

@RunWith(Cucumber.class) @CucumberOptions(plugin = { "com.consol.citrus.cucumber.CitrusReporter" } )public class VotingRestFeatureIT { }

@cucumberbdd

Page 25: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Messaging FeatureFeature: Voting Http REST API Background: Given Voting list is empty And New voting "Do you like donuts?" And voting options are "yes:no" Scenario: Top vote When client creates the voting And client votes for "no" Then votes should be | yes | 0 | | no | 1 | And top vote should be "no" Scenario: Close voting Given reporting is enabled When client creates the voting And client votes for "yes" 3 times And client votes for "no" 2 times And client closes the voting Then participants should receive reporting mail"""Dear participants,the voting '${title}' came to an end.The top answer is 'yes'!Have a nice day!Your Voting-App Team"""

@citrus_test@cucumberbdd

Page 26: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Endpoint injection

public class VotingRestSteps { @CitrusEndpoint private HttpClient votingClient;

@CitrusEndpoint private MailServer mailServer; @CitrusResource private TestRunner runner; @Given("^Voting list is empty$") public void clear() { runner.http(action -> action.client(votingClient) .send() .delete("/voting")); runner.http(action -> action.client(votingClient) .receive() .response(HttpStatus.OK)); } }

@citrus_test@cucumberbdd

Page 27: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Endpoint configuration

@Configurationpublic class CitrusEndpointConfig { @Bean public HttpClient votingClient() { return CitrusEndpoints.http() .client() .requestUrl("http://localhost:8080/rest/services") .build(); } @Bean public MailServer mailServer() { return CitrusEndpoints.mail() .server() .port(2222) .autoStart(true) .autoAccept(true) .build(); }}

@citrus_test@cucumberbdd

Page 28: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Citrus endpoints

Component Descriptioncitrus-http Http REST client and servercitrus-jms JMS queue or topic destinationcitrus-ws SOAP client and server

citrus-mail SMTP mail client and servercitrus-docker Docker container managementcitrus-camel Apache Camel endpoint

citrus-selenium Selenium browser endpointcitrus-vertx Vert.x endpoint

citrus-kubernetes Kubernetes client…

@cucumberbdd @citrus_test

Page 29: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Http REST messaging@Given("^New voting \"([^\"]+)\"$") public void newVoting(String title) { runner.variable("id", "citrus:randomUUID()"); runner.variable("title", title); runner.variable("options", buildOptionsAsJsonArray("yes:no")); runner.variable("closed", false);} @When("^client creates the voting$") public void createVoting() { runner.http(action -> action.client(votingClient) .send() .post("/voting") .contentType("application/json") .payload("{ \"id\": \"${id}\", \"title\": \"${title}\", \"options\": ${options} }")); runner.http(action -> action.client(votingClient) .receive() .response(HttpStatus.OK) .messageType(MessageType.JSON));}

@citrus_test@cucumberbdd

Page 30: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Mail SMTP messaging@Then("^participants should receive reporting mail$") public void shouldReceiveReportingMail(String text) { runner.createVariable("mailBody", text); runner.receive(action -> action.endpoint(mailServer) .payload(new ClassPathResource("templates/mail.xml")) .header(CitrusMailMessageHeaders.MAIL_SUBJECT, "Voting results") .header(CitrusMailMessageHeaders.MAIL_FROM, "[email protected]") .header(CitrusMailMessageHeaders.MAIL_TO, "[email protected]"));}

@citrus_test

<mail-message xmlns="http://www.citrusframework.org/schema/mail/message"> <from>[email protected]</from> <to>[email protected]</to> <cc></cc> <bcc></bcc> <subject>Voting results</subject> <body> <contentType>text/plain; charset=us-ascii</contentType> <content>${mailBody}</content> </body> </mail-message>

@cucumberbdd

Page 31: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

JMS messaging

@CitrusEndpoint(name = "reportingEndpoint") private JmsEndpoint reportingEndpoint;

@Then("^reporting should receive vote results$") public void shouldReceiveReport(DataTable dataTable) { runner.createVariable("results", buildOptionsAsJsonArray(dataTable)); runner.receive(action -> action.endpoint(reportingEndpoint) .messageType(MessageType.JSON) .payload("{ \"id\": \"${id}\", \"title\": \"${title}\", \"options\": ${results} }"));}

@citrus_test

@Beanpublic JmsEndpoint reportingEndpoint() { return CitrusEndpoints.jms() .asynchronous() .connectionFactory(connectionFactory()) .destination("jms.voting.report") .build();}

@cucumberbdd

Page 32: Behavior driven integration with Cucumber & Citrus

#DevoxxUS @citrus_test

Demo

@cucumberbdd

Page 33: Behavior driven integration with Cucumber & Citrus

#DevoxxUS

Reading & information

https://cucumber.io

http://citrusframework.org

Demo Sources

https://github.com/christophd/citrus-demo-devoxx-us

Citrus Cucumber Extension

http://citrusframework.org/reference/html/cucumber.html

@citrus_test@cucumberbdd

Page 34: Behavior driven integration with Cucumber & Citrus

#DevoxxUS @citrus_test

Thank You!

@cucumberbdd