Spring Boot Cucumber Integration Using Gradle

Cucumber

Cucumber is a BDD (behavior driven development) framework for running automated acceptance or integration test cases. Cucumber framework is written in Ruby language, but it is also available in other programming language. Cucumber uses Gherkin parser but the implementation is done using the target language.

Cucumber allows the execution of feature documentation written in business-facing text. Cucumber works with Ruby, Java, .NET, Flex or web applications written in any language. It has been translated to over 40 spoken languages. – http://cukes.info/

Prerequisites

Java 1.8+, Spring Boot 2.7.4, Cucumber 7.8.0, Junit 4.13.2, RestAssured 5.2.0, Gradle 7.4.2

Project Setup

The gradle version 7.x creates a different project structures than earlier versions. So, the project will have a lib directory where Java related files will be available. The name of the project is springboot-cucumber-gradle.

The following build.gradle script is used for this example:

buildscript {
	ext {
		springBootVersion = '2.7.4'
	}
    repositories {
    	mavenLocal()
    	mavenCentral()
    }
    dependencies {
    	classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

plugins {
    id 'java-library'
    id 'org.springframework.boot' version "${springBootVersion}"
}

repositories {
	mavenLocal()
    mavenCentral()
}

dependencies {
	implementation("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
	testImplementation("org.springframework.boot:spring-boot-starter-test:${springBootVersion}") {
		exclude group: 'org.mockito'
		exclude group: 'org.junit.jupiter'
		exclude group: 'org.junit.vintage'
	}
	testImplementation 'io.cucumber:cucumber-spring:7.8.0'
    testImplementation 'io.cucumber:cucumber-java:7.8.0'
    testImplementation 'io.cucumber:cucumber-junit:7.8.0'
    testImplementation 'io.rest-assured:rest-assured:5.2.0'
    testImplementation 'junit:junit:4.13.2'
}

/*test {
  	// Use old (Junit 4) runner since Cucumber doesn't support Junit 5
  	useJUnit()
}*/

I am using the required dependencies for this example, such as, Cucumber, Rest Assured, Junit, etc.

Rest Controller

A simple REST controller spring boot class that has a GET API which can be accessible in browser also. The GET API while accessed via REST Client or browser, give the response as “Hello”.

@RestController
@SpringBootApplication
public class CucumberApp {

	public static void main(String[] args) {
		SpringApplication.run(CucumberApp.class, args);
	}

	@GetMapping("/hello")
	public ResponseEntity<String> hello() {
		return new ResponseEntity<String>("Hello", HttpStatus.OK);
	}

}

Now I will write cucumber related feature file, runner class, step definition class.

Feature File

feature file is mainly meant for non-technical people and they can understand what is going to be tested for the application.

The following feature file is a simple one and has a single scenario to check the status code of the REST response.

The following content is written into a file Hello.feature in the folder src/test/resoutrces/features.

Feature: Validation of get method

  Scenario: Send a valid Request to get hello
  
  Given I send a request to the URL to get hello
  Then the response will return status 200

Runner Class

The runner class is responsible for running the cucumber test cases. If any unimplemented feature or scenario is found in the step definition class or step definition class is not found for the corresponding the feature or feature file, then this class will generate the skeleton of the step definition class.

The following runner is written with the following configuration:

@RunWith(Cucumber.class)
@CucumberOptions(plugin = { "pretty", "html:target/hello.json" }, features = "classpath:features")
public class CucumberRunnerTest {

}

Step Definition Class

Step definition class is used to define the implementation for the feature or scenario written into the feature file.

If you think that step definition would be difficult to implement, then you can run the above runner class to generate the skeleton for the step definition class.

Running the above runner class will produce the following skeleton:

io.cucumber.junit.UndefinedStepException: The step 'I send a request to the URL to get hello' and 1 other step(s) are undefined.
You can implement these steps using the snippet(s) below:

@Given("I send a request to the URL to get hello")
public void i_send_a_request_to_the_url_to_get_hello() {
    // Write code here that turns the phrase above into concrete actions
    throw new io.cucumber.java.PendingException();
}
@Then("the response will return status {int}")
public void the_response_will_return_status(Integer int1) {
    // Write code here that turns the phrase above into concrete actions
    throw new io.cucumber.java.PendingException();
}

Now you can copy the above @Given and @Then steps into the step definition class.

Remember the runner class is run as a Junit test class.

I have created a class HelloStep.java and copied the above content to implement the rest of the logic:

public class HelloStep {

	private ValidatableResponse validatableResponse;
	private final String endpoint = "http://localhost:8080/hello";

	@Given("I send a request to the URL to get hello")
	public void i_send_a_request_to_the_url_to_get_hello() {
		validatableResponse = RestAssured.given().contentType(ContentType.JSON).when().get(endpoint).then();
	}

	@Then("the response will return status {int}")
	public void the_response_will_return_status(Integer statusCode) {
		validatableResponse.assertThat().statusCode(statusCode);
	}

}

To test your cucumber test cases, you need to execute the runner class.

Finally, when you run the runner class, you will get the connection refused or connection timeout error as your application is running on Tomcat server at port 8080. You have two options, either you run the application manually then execute the runner class or write a spring-cucumber configuration class so that you don’t need to run the application manually.

As it is an integration test case, so I will create a cucumber-spring configuration class which will start the application before the test case is run.

Spring Cucumber Configuration

The following class will start the application before cucumber test is invoked and the application will be shut down as soon as test is finished.

@CucumberContextConfiguration
@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
@ContextConfiguration(classes = CucumberApp.class, loader = SpringBootContextLoader.class)
public class CucumberSpringContextConfiguration {

	private static final Logger LOG = LoggerFactory.getLogger(CucumberSpringContextConfiguration.class);

	/**
	 * Need this method so the cucumber will recognize this class as glue and load
	 * spring context configuration
	 */
	@Before
	public void setUp() {
		LOG.info("-------------- Spring Context Initialized For Executing Cucumber Tests --------------");
	}

}

Test Result

Once you run the runner class, your test will pass and you will get the test report in the location: springboot-cucumber-gradle\lib\build\reports\tests\test\index.html.

cucumber spring boot integration test

Hope you got an idea how to write integration test for spring boot with cucumber framework.

Source Code

Download

Leave a Reply

Your email address will not be published. Required fields are marked *