2

Overview

A maven project using spring boot for which some cucumber tests are implemented (in the same project!).

src
 |
 |-main
 |  |
 |  |-java
 |     |
 |     |-SpringBootApp
 |     |-Controller
 |
 |-test
   |
   |-java
   |  |
   |  |-cucumbertests
   |       |     
   |       |-StepDefinitions
   |       |-CucumberTestsRunner
   |
   |-resources
        |-features
              |-hello.feature

Controller

@RestController
@RequestMapping("/")
public class Controller {

    @GetMapping("hello")
    public String hello() {
        return "Hello!";
    }
}

CucumberTestsRunner

@RunWith(Cucumber.class)
@CucumberOptions(glue = "cucumbertests")
pulic class CucumberTestsRunner {
}

StepDefinitions

public class StepDefinitions {

    private String response;

    @When("I say hello")
    public void iSayHello() {
        // rest assured call
        response = get("<base_url>/hello").extract().asString();
    }

    @Then
    public void iMGreetedWithHello() {
        assertEquals("Hello!", response);
    }
}

With this in place,

  • I can run in a console mvn spring-boot:run (from where the SpringBoot application starts)
  • and then in another console mvn test -Dtest=CucumberTestsRunner, from where the Cucumber tests run against the webservices

So far so good, the tests pass without any issue.

Problem

I'd like to be able to issue a single command to start the SpringBoot application and then run the tests against the started application. Then after the tests finished, kill the SpringBoot application.

Ideally this is intended to be used in a CI system like Jenkins.

I was exploring the Apache Maven AntRun plugin as an option, but this is my first time doing this kind of set up and I'm not sure this would be a right approach. Previously I have seen set up like this but on independent projects (tests in a different app than the tested application).

9
  • Is there a good reason why you are using cucumber tests instead of usual unit/integration tests for a Spring Boot application? Commented Aug 17, 2020 at 8:08
  • @khmarbaise there are unit tests in the project, but we also have Cucumber tests for different purposes. Commented Aug 17, 2020 at 8:13
  • 2
    You should control the cucumber test by using the maven failsafe plugin instead of maven-surefire-plugin which means they will be executed after the package phase which means the whole application is already being built (jar file exist) so you can start your application via ProcessBuilder and run your tests against it.. Apart from that I would think about going to JUnit Jupiter instead of Cucumber cause Cucumber only supports JUnit 4(as far as I know)..which limits several parts. Commented Aug 17, 2020 at 8:26
  • 1
    Have a look at spring-boot:start and spring-boot:stop goals. You could have your tests run in between those two. Commented Aug 17, 2020 at 8:49
  • 1
    @khmarbaise Cucumber supports JUnit 5. Commented Aug 17, 2020 at 17:31

2 Answers 2

3

As an alternative to using mvn spring-boot:start test spring-boot:stop or to using mvn verify and starting the application in the pre-integration-test and stopping it in the post-integration-test phase you can also use cucumber-spring and run the test with Mock MCV.

├── pom.xml
└── src
    ├── main
    │   └── java
    │       └── com
    │           └── example
    │               └── Application.java
    └── test
        ├── java
        │   └── com
        │       └── example
        │           └── CucumberTest.java
        └── resources
            ├── com
            │   └── example
            │       └── hello.feature
            └── junit-platform.properties

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.1.RELEASE</version>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>com.example</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <properties>
        <java.version>11</java.version>
        <cucumber.version>6.5.0</cucumber.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </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-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>${cucumber.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-spring</artifactId>
            <version>${cucumber.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-junit-platform-engine</artifactId>
            <version>${cucumber.version}</version>
            <scope>test</scope>
        </dependency>

    </dependencies>
</project>
package com.example;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class Application {

    @RestController
    public static class HelloController {

        @RequestMapping("/")
        public String local() {
            return "Greetings from Local!";
        }

    }

}

package com.example;

import io.cucumber.java.en.Given;
import io.cucumber.junit.platform.engine.Cucumber;
import io.cucumber.spring.CucumberContextConfiguration;
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.web.servlet.MockMvc;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@Cucumber
@CucumberContextConfiguration
@SpringBootTest
@AutoConfigureMockMvc
public class CucumberTest {

    @Autowired
    private MockMvc mvc;

    @Given("the application says hello")
    public void getLocalHello() throws Exception {
        mvc.perform(get("/").accept(APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("Greetings from Local!")));
    }

}

Feature: Hello world

  Scenario: Calling a rest end point
    * the application says hello

Sign up to request clarification or add additional context in comments.

Comments

2

Instead of using spring-boot:run in one console and running your tests in a second one, you can use use spring-boot:start and spring-boot:stop.

Something like mvn spring-boot:start test spring-boot:stop would start the application, run the tests and then stop the application again.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.