【测试相关】Cucumber与Junit4集成示例,以及与Spring Boot+Junit5集成示例

1,748 阅读5分钟

【本文目标】
本文主要有三个demo:

  • 使用Java运行Cucumber。
  • Cucumber与Junit4的集成。
  • Cucumber与Spring Boot 2.5.7 + Junit5的集成。

1. Cucumber介绍

官网:cucumber.io/

Cucumber是一个BDD的测试框架,BDDBehavior-Driven Development(行为驱动测试),即以功能使用者的角度,编写需求场景。

Cucumber并不是java独有的,而是支持很多种语言,如js, ruby, c++, go等等。总体上来说,它是一种测试思想(BDD)的实现。

一个系统 --> 很多个feature(即功能) --> 基于每个功能编写的测试

比如我们写了一个功能,叫验证今天是不是星期五。那么我们在编写Cucumber feature文件的时候,就可以写:

Feature: Is it Friday yet?
  Everybody wants to know when it's Friday

  Scenario: Sunday isn't Friday
    Given today is Sunday
    When I ask whether it's Friday yet
    Then I should be told "Nope"

可以看到Cucumber的feature定义文件,由若干个关键字组成,如Feature, Scenerio, Given, When, Then...等等。这些关键字是Gherkin语法,如同其它语言的语法一样,都是有特定的规则的,只需要在定义的时候遵循即可。

另外,我们也可以看到这样的feature定义,就很直白,它建立起开发人员与非技术人员(如产品经理)之间的桥梁,即可以清楚的通过behavior来驱动功能上的测试。我想,这就是cucumber流行起来的原因之一吧。

2. Cucumber在IDE中的插件

可以预先安装下Cucumber的插件:Cucumber for Java,这个插件可以识别feature中的语法。 image.png

3. 与Java的集成

可以通过一个具体的例子来看看Cucumber是如何运行的。 首先是依赖,主要依赖的包有cucumber-java,这个包主要是可以把cucumber的feature文件中的Gherkin语法转成java认识的。

<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-java</artifactId>
    <version>7.6.0</version>
</dependency>

参考官网中的例子:cucumber.io/docs/guides…

在resources下新建目录features,新建文件:is_it_friday_yet.feature

Feature: Is it Friday yet?
  Everybody wants to know when it's Friday

  Scenario: Sunday isn't Friday
    Given today is Sunday
    When I ask whether it's Friday yet
    Then I should be told "Nope"

除了feature文件,我们需要写java代码来实现上述的feature:

public class Stepdefs {
    private String today;
    private String actualAnswer;

    @Given("today is Sunday")
    public void today_is_Sunday() {
        today = "Sunday";
    }

    @When("I ask whether it's Friday yet")
    public void i_ask_whether_it_s_Friday_yet() {
        actualAnswer = IsItFriday.isItFriday(today);
    }

    @Then("I should be told {string}")
    public void i_should_be_told(String expectedAnswer) {
        System.out.println(actualAnswer.equals(expectedAnswer) ? "Pass" : "Failed");
    }

}

【运行feature文件】: image.png

【运行结果】: image.png

4. 与JUnit4集成

JUnit分版本4和5,这里介绍的是Cucumber与4的集成。

在第3章介绍的Cucumber hello world例子中,我们需要手动运行feature文件,但实际项目中,我们需要在CI阶段(通常是在Jenkins中通过maven去编绎在git中的代码),而在mvn package的过程中,我们希望maven不仅编译运行单元测试,还要运行cucumber相关的测试。

4.1 首先是依赖介绍

除了上述的cucumber-java包外,还需要引入cucumber-junit的依赖:

<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-junit</artifactId>
    <version>7.6.0</version>
    <scope>test</scope>
</dependency>

cucumber-junit依赖默认会引入junit-4.13.2,由此可以看出,这里与junit版本4集成的: image.png

4.2 Junit文件的编写

新建java类:CucumberIT.java

  • @RunWith就是Junit4中最常见的运行器(Junit5用的是@ExtendWith)。
  • @RunWith的参数Cucumber.class就是Junit的入口类,类似Spring中的SpringRunner.java)。
  • @CucumberOptions可以指定一些Cucumber的配置类,这里指定了feature文件所在的路径。
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(features="src/main/resources/features")
public class CucumberIT {
}

建好文件后,我们可以运行这个文件,可以看到is_it_friday_yet.feature会被执行。 image.png

4.3 集成到mvn test goal中

除了jar包的引入和编写Junit入口文件,我们还需要告诉maven在test阶段,还需要运行上述的Junit入口类——CucumberIT.java。

默认情况下,我们运行mvn test会执行plugin=maven-surefire-plugin,这里我们需要显式的引入这个插件,并且额外加上上述的junit入口类。

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>3.0.0-M5</version>
            <configuration>
                <includes>
                    **/CucumberIT*.java
                </includes>
            </configuration>
        </plugin>
    </plugins>
</build>

image.png

5. 与Spring Boot + JUnit5 集成

参考:

网上很多Cucumber的例子,但很多都是与Junit4的集成(我在第4章介绍了)。所以本章介绍Cucumber与Spring Boot + Junit5的集成。

5.1 依赖

  • 首先是Spring boot相关的依赖:web starter和test starter。
  • 接着就是CucumberJunit5相关的依赖,使用的是dependencyManagement,更为方便的管理。
  • 依赖的cucumber-junit-platform-engine,则是cucumberjunit5的依赖,而第4章与junit4的集成,用的是cucumber-junit
<!-- 引用的是Spring Boot 2.5.7 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.7</version>
</parent>

<properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-bom</artifactId>
            <version>7.6.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.junit</groupId>
            <artifactId>junit-bom</artifactId>
            <version>5.9.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <!-- Spring相关的依赖:引用的是web starter以及test starter -->
    <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>

    <!-- Cucumber 相关 -->
    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-java</artifactId>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-spring</artifactId>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-junit-platform-engine</artifactId>
        <scope>test</scope>
    </dependency>
    
    <!-- JUnit5 相关 -->
    <dependency>
        <groupId>org.junit.platform</groupId>
        <artifactId>junit-platform-suite</artifactId>
        <scope>test</scope>
    </dependency>

</dependencies>

5.2 Spring boot相关的类

  • 首先创建Spring boot启动类,使用注解:@SpringBootApplication。主要为了使用@SpringBootTest的时候,可以自动引用该启动类,以便使用@ComponentScan
  • 其次创建UserService,使用注解@Service。主要是为了在BDD的Steps类中测试使用@Autowired来引用Spring bean。

5.3 Cucumber相关的资源

这个和第3章一样:

  • /src/main/resources/festures下:is_it_friday_yet.feature
  • Stepdefs.java类,在这个类中,我们可以使用@Autowired来引入上述的UserService,以便可以测试Spring context是否生效!!!

5.4 新建测试相关类

首先是新建与Spring集成的类:CucumberBootstrap.java

  • @CucumberContextConfiguration注解告诉Cucumber需要使用Spring的context configuration。
  • @SpringBootTest,在###5.2中有介绍。
@CucumberContextConfiguration
@SpringBootTest
public class CucumberBootstrap {
}

另外一个就是重要的类就是Cucumber与Junit5的集成的类,主要是通过这个类,Junit5可以找到Cucumber的features的文件,然后执行bdd测试。

这里如果使用**Test结尾,就不需要像第4章一样,在maven插件maven-surefire-plugin中显式的引用这个类,因为插件默认会include以Test结尾的文件。

有些文章中写Cucumber与Junit5的集成,使用的注解还是@RunWith(Cucumber.class),那么这种就还是与Junit4的集成了。

还有些文章中会介绍使用注解@Cucumber,该注解位于包io.cucumber.junit.platform.engine.Cucumber中,截至我写文章的时间,这个类已经被mark成@Deprecated了,点进去看,会推荐使用@Suite。这就是为啥下述使用@Suite,而不是@Cucumber的原因。

@Suite
@SelectClasspathResource("features")
public class CucumberTest {
}

5.5 运行测试

可以通过运行CucumberTest.java来跑Cucumber的features文件,也可使用mvn test来跑全部的features文件。

结果: image.png