BAEL-4783 rename module and fix indents

This commit is contained in:
Trixi Turny 2021-04-19 20:25:33 +01:00
parent c49ffcc3cd
commit af9cc0deb9
21 changed files with 792 additions and 0 deletions

View File

@ -0,0 +1,182 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-2</relativePath>
<!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cucumber</artifactId>
<version>1.0-SNAPSHOT</version>
<name>cucumber</name>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<cucumber.version>6.9.1</cucumber.version>
<cucumber-reporting.version>5.4.0</cucumber-reporting.version>
<selenium.version>3.141.59</selenium.version>
<webdrivermanager.version>4.3.1</webdrivermanager.version>
<webjars-locator.version>0.40</webjars-locator.version>
<jquery.version>3.0.0</jquery.version>
<bootstrap.version>4.5.3</bootstrap.version>
<spring-cloud-contract-wiremock.version>2.2.5.RELEASE</spring-cloud-contract-wiremock.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
<version>${webjars-locator.version}</version>
</dependency>
<dependency>
<groupId>org.webjars.npm</groupId>
<artifactId>jquery-slim</artifactId>
<version>${jquery.version}</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>${bootstrap.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit-platform-engine</artifactId>
<version>${cucumber.version}</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-spring</artifactId>
<version>${cucumber.version}</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-support</artifactId>
<version>${selenium.version}</version>
</dependency>
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>${webdrivermanager.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>spring-mock-mvc</artifactId>
<version>${rest-assured.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>json-schema-validator</artifactId>
<version>${rest-assured.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-wiremock</artifactId>
<version>${spring-cloud-contract-wiremock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>net.masterthought</groupId>
<artifactId>maven-cucumber-reporting</artifactId>
<version>${cucumber-reporting.version}</version>
<configuration>
<projectName>randomnumbergenerator</projectName>
<outputDirectory>
${project.build.directory}
</outputDirectory>
<inputDirectory>
${project.build.directory}/cucumber
</inputDirectory>
<classificationFiles>**/*.properties</classificationFiles>
<jsonFiles>
<param>**/*.json</param>
</jsonFiles>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<profiles>
<profile>
<id>acceptance</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${maven-failsafe-plugin.version}</version>
<configuration>
<includes>
<include>**/*IT.java</include>
</includes>
</configuration>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</plugin>
<plugin>
<groupId>net.masterthought</groupId>
<artifactId>maven-cucumber-reporting</artifactId>
<executions>
<execution>
<id>execution</id>
<phase>post-integration-test</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,13 @@
package com.baeldung.cucumber_tags;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@ -0,0 +1,17 @@
package com.baeldung.cucumber_tags.controller;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HealthController {
@RequestMapping(value="/status", produces= MediaType.APPLICATION_JSON_VALUE)
public HttpStatus statusCheck() {
return ResponseEntity.ok().build().getStatusCode();
}
}

View File

@ -0,0 +1,36 @@
package com.baeldung.cucumber_tags.controller;
import com.baeldung.cucumber_tags.service.RandomNumberGeneratorService;
import lombok.Data;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class UiController {
@GetMapping("/random-number-generator")
public String showForm(Model model) {
RandomNumberQuery randomNumberQuery = new RandomNumberQuery();
model.addAttribute("randomNumberQuery", randomNumberQuery);
return "random-number-generator";
}
@PostMapping(value = "/random-number-generator")
public String generateRandomNumber(@ModelAttribute("randomNumberQuery") final RandomNumberQuery randomNumberQuery) {
RandomNumberGeneratorService service = new RandomNumberGeneratorService();
randomNumberQuery.randomNumber = service.generateRandomNumber(randomNumberQuery.min, randomNumberQuery.max);
return "random-number-generator";
}
@Data
private static class RandomNumberQuery {
Integer min = null;
Integer max = null;
Integer randomNumber = null;
}
}

View File

@ -0,0 +1,10 @@
package com.baeldung.cucumber_tags.service;
import java.util.concurrent.ThreadLocalRandom;
public class RandomNumberGeneratorService {
public int generateRandomNumber(int min, int max) {
return ThreadLocalRandom.current().nextInt(min, max + 1);
}
}

View File

@ -0,0 +1,53 @@
<!DOCTYPE HTML>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Random Number Generator</title>
<link rel="stylesheet" th:href="@{webjars/bootstrap/css/bootstrap.min.css}"/>
</head>
<body>
<nav class="navbar navbar-light bg-light">
<div class="container">
<h1 class="navbar-brand mb-10 h1">Random Number Generator</h1>
</div>
</nav>
<div role="main" class="container">
<div>
<form id="random" name="random" action="#" th:action="@{/random-number-generator}"
th:object="${randomNumberQuery}" method="post">
<div class="row mt-10">
<label for="min" class="col-sm-1 control-label">Min:</label>
<div class="col-sm-3">
<input type="text" id="min" class="form-control" th:field="*{min}" placeholder="min" required
autofocus>
</div>
<label for="max" class="col-sm-1 control-label">Max:</label>
<div class="col-sm-3">
<input type="text" id="max" class="form-control" th:field="*{max}" placeholder="max" required
autofocus>
</div>
<div class="col-sm-3">
<button id="generate" type="submit" class="btn btn-primary">Generate</button>
</div>
</div>
</form>
<div class="row mt-10">
<p>Result: </p>
<p id="result" class="display-1 font-weight-bold" th:text="${randomNumberQuery.randomNumber}"></p>
</div>
<script type="text/javascript" th:src="@{webjars/bootstrap/js/bootstrap.min.js}"></script>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,9 @@
package com.baeldung.cucumber_tags.acceptance;
import io.cucumber.junit.platform.engine.Cucumber;
@Cucumber()
public class AcceptanceTestRunnerIT {
}

View File

@ -0,0 +1,37 @@
package com.baeldung.cucumber_tags.acceptance.api.steps;
import com.baeldung.cucumber_tags.acceptance.commonutil.ScenarioContextApi;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import org.hamcrest.Matchers;
import org.springframework.beans.factory.annotation.Autowired;
public class HealthSteps {
@Autowired
private ScenarioContextApi context;
@When("^I make a GET call on ([^\"]*)$")
public void iMakeAGETCallOn(String path) {
context.invokeHttpGet(path);
}
@When("^I make a POST call on ([^\"]*)$")
public void iMakeAPOSTCallOn(String path) {
context.invokeHttpPost(path, context.postBody);
}
@Then("^I should receive (\\d+) response status code$")
public void iShouldReceiveStatusCodeResponse(int code) {
context.response.then().statusCode(code);
}
@Then("^should receive a non-empty body$")
public void shouldReceiveANonEmptyBody() {
context.response.then().body(Matchers.notNullValue());
}
}

View File

@ -0,0 +1,44 @@
package com.baeldung.cucumber_tags.acceptance.commonutil;
import org.openqa.selenium.InvalidArgumentException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Optional;
public final class CucumberEnvironment {
private static final String SELENIUM_GRID_URL_ENV_VAR = "SELENIUM_GRID_URL";
private static final String SERVICE_HOSTNAME = "SERVICE_HOSTNAME";
private static final String LOCALHOST = "localhost";
private CucumberEnvironment() {
}
/**
* Gets the network host where the service is running. When running while developing, this is going to be
* localhost because webdriver and the test runner are going to be on the same machine. On gitlab-ci /
* docker-compose though, they are going to be on separate hosts so webdriver needs to hit the
* machine where the test runner is starting the service, not localhost.
*/
public static String getServiceHost() {
final Optional<String> serviceHostname = Optional.ofNullable(System.getenv(SERVICE_HOSTNAME));
return serviceHostname.orElse(LOCALHOST);
}
/**
* Gets the url where the selenium grid instance is. This is used for gitlab-ci / docker-compose setups.
*/
public static Optional<URL> getSeleniumGridUrl() {
final Optional<String> seleniumGridUrlString = Optional.ofNullable(System.getenv(SELENIUM_GRID_URL_ENV_VAR));
return seleniumGridUrlString.map(CucumberEnvironment::parseSeleniumGridUrl);
}
private static URL parseSeleniumGridUrl(String seleniumGridUrlString) {
try {
return new URL(seleniumGridUrlString);
} catch (MalformedURLException e) {
throw new InvalidArgumentException(SELENIUM_GRID_URL_ENV_VAR + "env var is not a valid URL");
}
}
}

View File

@ -0,0 +1,57 @@
package com.baeldung.cucumber_tags.acceptance.commonutil;
import io.restassured.http.ContentType;
import io.restassured.module.mockmvc.response.MockMvcResponse;
import io.restassured.module.mockmvc.specification.MockMvcRequestSpecification;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import static io.restassured.module.mockmvc.RestAssuredMockMvc.given;
@Component
@Scope(scopeName = "cucumber-glue")
public class ScenarioContextApi {
@LocalServerPort
int port;
private ScenarioReport report;
public MockMvcRequestSpecification request;
public MockMvcResponse response;
public Map<String, Object> postBody = new HashMap<>();
public Map<String, Object> queryParams = new HashMap<>();
public ScenarioContextApi() {
reset();
}
private void reset() {
report = new ScenarioReport();
request = null;
response = null;
postBody.clear();
queryParams.clear();
}
public void invokeHttpGet(String path, Object... pathParams) {
request = given().log().all();
response = request.when().get(path, pathParams);
response.then().log().all();
}
public void invokeHttpPost(String path, Map<String, ?> data) {
request = given().log().all().body(data).queryParams(queryParams).contentType(ContentType.JSON);
response = request.post(path);
response.then().log().all();
}
public ScenarioReport getReport() {
return report;
}
}

View File

@ -0,0 +1,77 @@
package com.baeldung.cucumber_tags.acceptance.commonutil;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.net.URL;
import static io.github.bonigarcia.wdm.WebDriverManager.getInstance;
import static io.github.bonigarcia.wdm.config.DriverManagerType.CHROME;
@Component
@Scope(scopeName = "cucumber-glue")
public class ScenarioContextUI {
protected static final String RANDOM_NUMBER_URL = "/random-number-generator";
@LocalServerPort
int port;
private WebDriver driver;
private ScenarioReport report;
public ScenarioContextUI() {
reset();
}
private void reset() {
report = new ScenarioReport();
driver = null;
}
private static WebDriver getRemoteWebDriver(URL url) {
return new RemoteWebDriver(url, DesiredCapabilities.chrome());
}
private static WebDriver getLocalChromeDriver() {
getInstance(CHROME).setup();
return new ChromeDriver();
}
public ScenarioReport getReport() {
return report;
}
public String getRandomNumberUrl() {
return "http://" + getServiceBaseUrl() + RANDOM_NUMBER_URL;
}
private String getServiceBaseUrl() {
return CucumberEnvironment.getServiceHost() + ":" + Integer.toString(port);
}
/**
* If we are running inside docker (mostly for gitlab ci purposes), we expect a selenium grid setup.
* If that environment variable isn't set, we assume we're in "dev mode" and ChromeDriverManager will
* provide the local instance of chromedriver (no need to have chromedriver installed).
*/
public WebDriver getWebDriver() {
if (driver == null) {
return getFreshWebdriver();
} else {
return driver;
}
}
private WebDriver getFreshWebdriver() {
driver = CucumberEnvironment.getSeleniumGridUrl()
.map(ScenarioContextUI::getRemoteWebDriver)
.orElseGet(ScenarioContextUI::getLocalChromeDriver);
return driver;
}
}

View File

@ -0,0 +1,58 @@
package com.baeldung.cucumber_tags.acceptance.commonutil;
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.Scenario;
import io.cucumber.spring.CucumberContextConfiguration;
import io.restassured.config.LogConfig;
import io.restassured.module.mockmvc.RestAssuredMockMvc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import java.io.IOException;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles(profiles = {"acceptance-test"})
@AutoConfigureMockMvc
@CucumberContextConfiguration
public class ScenarioHooks {
@Autowired
private ScenarioContextUI uiContext;
@Autowired
private ScenarioContextApi apiContext;
@Autowired
private MockMvc mvc;
@Before("@ui")
public void setupForUI() {
uiContext.getWebDriver();
}
@After("@ui")
public void tearDownForUi(Scenario scenario) throws IOException {
uiContext.getReport().write(scenario);
uiContext.getReport().captureScreenShot(scenario, uiContext.getWebDriver());
uiContext.getWebDriver().quit();
}
@Before("@api")
public void setupForApi() {
RestAssuredMockMvc.mockMvc(mvc);
RestAssuredMockMvc.config = RestAssuredMockMvc.config()
.logConfig(new LogConfig(
apiContext.getReport().getRestLogPrintStream(),
true));
}
@After("@api")
public void tearDownForApi(Scenario scenario) throws IOException {
apiContext.getReport().write(scenario);
}
}

View File

@ -0,0 +1,44 @@
package com.baeldung.cucumber_tags.acceptance.commonutil;
import io.cucumber.java.Scenario;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
public class ScenarioReport {
private List<String> messages = new ArrayList<>();
private ByteArrayOutputStream restLogOutputStream = new ByteArrayOutputStream();
public void addMessage(String message) {
messages.add(message);
}
public PrintStream getRestLogPrintStream() {
return new PrintStream(restLogOutputStream);
}
public void write(Scenario scenario) throws IOException {
for (String msg : messages) {
scenario.log(msg);
}
scenario.log(restLogOutputStream.toString());
restLogOutputStream.close();
}
public void captureScreenShot(Scenario scenario, WebDriver driver) {
try {
final byte[] screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
scenario.attach(screenshot, "image/png", "test");
} catch (Exception exception) {
scenario.log(exception.toString());
}
}
}

View File

@ -0,0 +1,28 @@
package com.baeldung.cucumber_tags.acceptance.ui.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.openqa.selenium.support.ui.ExpectedConditions.titleIs;
public class Page {
private static final long DEFAULT_WAIT_SECONDS = 5;
private static Page currentPage;
final WebDriver driver;
Page(WebDriver driver, String title) {
currentPage = this;
this.driver = driver;
getWait().until(titleIs(title));
}
public static <T extends Page> T getPage(Class<T> pageClass) {
return pageClass.cast(checkNotNull(currentPage));
}
WebDriverWait getWait() {
return new WebDriverWait(driver, DEFAULT_WAIT_SECONDS);
}
}

View File

@ -0,0 +1,41 @@
package com.baeldung.cucumber_tags.acceptance.ui.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class RandomNumberGeneratorPage extends Page {
@FindBy(id = "min")
private WebElement minField;
@FindBy(id = "max")
private WebElement maxField;
@FindBy(id = "generate")
private WebElement generateButton;
@FindBy(id = "result")
private WebElement result;
public RandomNumberGeneratorPage(WebDriver driver) {
super(driver, "Random Number Generator");
}
public void enterMinField(String min) {
minField.sendKeys(min);
}
public void enterMaxField(String max) {
maxField.sendKeys(max);
}
public void pressGenerateButton() {
generateButton.click();
}
public String getResult() {
return result.getText();
}
}

View File

@ -0,0 +1,49 @@
package com.baeldung.cucumber_tags.acceptance.ui.steps;
import com.baeldung.cucumber_tags.acceptance.commonutil.ScenarioContextUI;
import com.baeldung.cucumber_tags.acceptance.ui.pages.RandomNumberGeneratorPage;
import io.cucumber.java.en.And;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import org.openqa.selenium.support.PageFactory;
import org.springframework.beans.factory.annotation.Autowired;
import static com.baeldung.cucumber_tags.acceptance.ui.pages.Page.getPage;
import static org.junit.Assert.assertTrue;
public class RandomNumberGeneratorSteps {
@Autowired
private ScenarioContextUI context;
@Given("we are expecting a random number between min and max")
public void expectingRandomNumberBetweenMinAndMax() {
}
@And("I am on random-number-generator page")
public void iAmOnRandomNumberGeneratorPage() {
context.getWebDriver().get(context.getRandomNumberUrl());
PageFactory.initElements(context.getWebDriver(), RandomNumberGeneratorPage.class);
}
@When("^I enter min ([^\"]*)$")
public void whenIenterMin(String min) {
getPage(RandomNumberGeneratorPage.class).enterMinField(min);
}
@When("^I enter max ([^\"]*)$")
public void whenIenterMax(String max) {
getPage(RandomNumberGeneratorPage.class).enterMaxField(max);
}
@And("^I press Generate button")
public void pressScanButton() {
getPage(RandomNumberGeneratorPage.class).pressGenerateButton();
}
@Then("I should receive a random number between {int} and {int}")
public void iShouldReceiveARandomNumberBetweenAnd(int min, int max) {
assertTrue(Integer.parseInt(getPage(RandomNumberGeneratorPage.class).getResult()) >= min && Integer.parseInt(getPage(RandomNumberGeneratorPage.class).getResult()) <= max);
}
}

View File

@ -0,0 +1,18 @@
package com.baeldung.cucumber_tags.service;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class RandomNumberGeneratorServiceUnitTest {
private final RandomNumberGeneratorService tested = new RandomNumberGeneratorService();
@Test
public void generateRandomNumberReturnsOK() {
int actual = tested.generateRandomNumber(1,5);
assertTrue(actual>=1 && actual<=5);
}
}

View File

@ -0,0 +1,7 @@
@api
Feature: Health check
Scenario: Should have a working health check
When I make a GET call on /status
Then I should receive 200 response status code
And should receive a non-empty body

View File

@ -0,0 +1,10 @@
@ui
Feature: UI - Random Number Generator
Scenario: Successfully generate a random number
Given we are expecting a random number between min and max
And I am on random-number-generator page
When I enter min 1
And I enter max 10
And I press Generate button
Then I should receive a random number between 1 and 10

View File

@ -0,0 +1 @@
cucumber.plugin=pretty, json:target/cucumber/cucumber.json

View File

@ -42,6 +42,7 @@
<module>junit-4</module>
<module>testing-libraries</module>
<module>powermock</module>
<module>spring-junit5-cucumber</module>
</modules>
</project>