spring boot整合Cucumber(BDD)的方法
本文介紹了spring boot整合Cucumber(BDD)的方法,分享給大家,具體如下:
1、新建一個(gè)springboot工程工程結(jié)構(gòu)如下:

2、添加pom依賴
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.chhliu</groupId>
<artifactId>spring-boot-cucumber</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-boot-cucumber</name>
<description>Demo project for Spring Boot and Cucumber</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<cucumber.version>1.2.4</cucumber.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.7</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-core</artifactId>
<version>${cucumber.version}</version>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-spring</artifactId>
<version>${cucumber.version}</version>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-junit</artifactId>
<version>${cucumber.version}</version>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
<executions>
<execution>
<phase>integration-test</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<classpathScope>test</classpathScope>
<mainClass>com.chhliu.test.CucumberTest.java</mainClass>
<arguments>
<argument>--plugin</argument>
<argument>pretty</argument>
<argument>--glue</argument>
<argument>src/test/resources/</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
2、編寫service接口及其實(shí)現(xiàn)類
package com.chhliu.service;
/**
* 模擬登錄
* @author chhliu
*
*/
public interface UserInfoServiceI {
boolean login(String username, String password, String confirmPassword);
}
package com.chhliu.service;
import org.springframework.stereotype.Service;
@Service("userInfoService")
public class UserInfoService implements UserInfoServiceI{
public boolean login(String username, String password, String confirmPassword){
return (username.equals("chhliu") && password.equals("123456") && confirmPassword.equals("123456"));
}
}
3、編寫feature文件
#language: zh-CN
#"zh-CN": {
# "but": "*|但是<",
# "and": "*|而且<|并且<|同時(shí)<",
# "then": "*|那么<",
# "when": "*|當(dāng)<",
# "name": "Chinese simplified",
# "native": "簡(jiǎn)體中文",
# "feature": "功能",
# "background": "背景",
# "scenario": "場(chǎng)景|劇本",
# "scenario_outline": "場(chǎng)景大綱|劇本大綱",
# "examples": "例子",
# "given": "*|假如<|假設(shè)<|假定<"
# }
@bank
功能:假如我在銀行取錢的時(shí)候,如果我登錄成功并且輸入的密碼正確,那么會(huì)顯示我的銀行卡余額,假如余額為50萬(wàn)
場(chǎng)景:銀行取錢
假如:我以"chhliu"登錄
并且:輸入的密碼為"123456"
當(dāng):確認(rèn)密碼也為"123456"時(shí)
那么:顯示銀行卡余額為"500000"
4、編寫測(cè)試類
package com.chhliu.test;
import org.junit.runner.RunWith;
import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;
/**
* @RunWith(Cucumber.class) 這是一個(gè)運(yùn)行器 ,指用Cucumber來(lái)運(yùn)行測(cè)試
* @CucumberOptions中的features,用于指定我們項(xiàng)目中要運(yùn)行的feature的目錄
* @CucumberOptions中的format,用于指定我們項(xiàng)目中要運(yùn)行時(shí)生成的報(bào)告,并指定之后可以在target目錄中找到對(duì)應(yīng)的測(cè)試報(bào)告
* @CucumberOptions中的glue,用于指定項(xiàng)目運(yùn)行時(shí)查找實(shí)現(xiàn)step定義文件的目錄
*
* 在實(shí)際項(xiàng)目中,隨著項(xiàng)目的進(jìn)行,一個(gè)測(cè)試工程可能由多個(gè)feature文件組成,并且每個(gè)feature文件中可能也是由多個(gè)scenario組成。默認(rèn)情況下,
* 每次運(yùn)行是運(yùn)行所有feature中的所有scenario。這樣可能導(dǎo)致正常情況下運(yùn)行一次測(cè)試腳本,需要非常長(zhǎng)的時(shí)間來(lái)等待測(cè)試結(jié)果。
* 但是實(shí)際過程中,測(cè)試用例是有優(yōu)先級(jí)等區(qū)分的。比如smokeTest、regressionTest等。或者有時(shí)候會(huì)有特別小部分的用例,比如等級(jí)是critical,
* 這些用例需要長(zhǎng)時(shí)間運(yùn)行來(lái)監(jiān)測(cè)系統(tǒng)是否沒有白頁(yè)或者頁(yè)面404等現(xiàn)象。
* 所以我們必須區(qū)分開所有的scenario,可以使我們?cè)趩?dòng)測(cè)試腳本時(shí),可以根據(jù)我們需要來(lái)運(yùn)行哪些模塊的scenaro。這時(shí)我們可以使用Tags
* 在Cucumber里Tag是直接在Feature、Scenari或Scenario Outline關(guān)鍵字前給feature或scenario添加任意數(shù)量的前綴為@的tags,多個(gè)tag用空格來(lái)分隔
* @author chhliu
*
*/
@RunWith(Cucumber.class)
@CucumberOptions(plugin = {"json:target/cucumber.json", "pretty"}, features = "src/test/resources")
public class CucumberTest {
}
5、運(yùn)行測(cè)試類,并對(duì)測(cè)試輸出的未定義步驟進(jìn)行完善
package com.chhliu.test;
import javax.annotation.Resource;
import org.junit.Assert;
import com.chhliu.service.UserInfoServiceI;
import cucumber.api.java.zh_cn.假如;
import cucumber.api.java.zh_cn.當(dāng);
import cucumber.api.java.zh_cn.那么;
public class Cucumber集成spring {
@Resource(name="userInfoService")
private UserInfoServiceI service;
private String username;
private String password;
private String confirmPassword;
@假如("^:我以\"([^\"]*)\"登錄$")
public void 我以_登錄(String arg1) throws Throwable {
this.username = arg1;
}
@假如("^:輸入的密碼為\"([^\"]*)\"$")
public void 輸入的密碼為(String arg1) throws Throwable {
this.password = arg1;
}
@當(dāng)("^:確認(rèn)密碼也為\"([^\"]*)\"時(shí)$")
public void 確認(rèn)密碼也為_時(shí)(String arg1) throws Throwable {
this.confirmPassword = arg1;
}
@那么("^:顯示銀行卡余額為\"([^\"]*)\"$")
public void 顯示銀行卡余額為(String arg1) throws Throwable {
boolean isLogin = service.login(username, password, confirmPassword);
if(isLogin){
System.out.println("登錄成功!查詢余額如下:"+arg1);
Assert.assertEquals("500000", arg1);
}
}
}
6、在測(cè)試步驟上添加注解支持
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration // 不加此注解,bean會(huì)注入不進(jìn)去
@SpringBootTest // 不加此注解會(huì)找不到bean
public class Cucumber集成spring{
}
7、測(cè)試結(jié)果
2 Scenarios (2 passed)
11 Steps (11 passed)
0m0.091s
8、整合注意點(diǎn)
spring boot與Cucumber整合的時(shí)候,有個(gè)地方需要注意,因?yàn)閟pring boot提倡去xml化,所以傳統(tǒng)方式下,Cucumber會(huì)讀取classpath下的cucumber.xml配置文件來(lái)初始化bean的方式,和spring整合后,就不能用這種方式了,需要使用@ContextConfiguration注解來(lái)實(shí)現(xiàn)類的加載,如果是需要加載配置文件的方式的話,可以如下使用:
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
如果使用注解的方式來(lái)整合的話,使用如下:
@ContextConfiguration(classes=SpringBootCucumberApplication.class)
或者直接
@ContextConfiguration
特別注意:@ContextConfiguration注解必加,否則會(huì)出現(xiàn)bean注入失敗
下面,我們從源碼來(lái)看下為什么會(huì)造成這種情況。
該部分涉及的代碼都在cucumber-spring包下的SpringFactory類中,重點(diǎn)我們看下下面這個(gè)類:
public void start() {// cucumber測(cè)試啟動(dòng)方法
if (stepClassWithSpringContext != null) {// 如果使用了@ContextConfiguration注解的話,此處不為null
testContextManager = new CucumberTestContextManager(stepClassWithSpringContext);
} else {// 否則stepClassWithSpringContext就為null,會(huì)進(jìn)入下面這個(gè)分支
if (beanFactory == null) {
beanFactory = createFallbackContext();// 這個(gè)方法是我們要跟的重點(diǎn)
}
}
notifyContextManagerAboutTestClassStarted();
if (beanFactory == null || isNewContextCreated()) {
beanFactory = testContextManager.getBeanFactory();
for (Class<?> stepClass : stepClasses) {
registerStepClassBeanDefinition(beanFactory, stepClass);
}
}
GlueCodeContext.INSTANCE.start();
}
我們?cè)趤?lái)跟下createFallbackContext方法:
private ConfigurableListableBeanFactory createFallbackContext() {
ConfigurableApplicationContext applicationContext;
if (getClass().getClassLoader().getResource("cucumber.xml") != null) {// 會(huì)先根據(jù)classpath下的cucumber.xml來(lái)初始化<span style="font-family:Arial, Helvetica, sans-serif;">ConfigurableApplicationContext</span>
applicationContext = new ClassPathXmlApplicationContext("cucumber.xml");
} else {// 如果沒有配置cucumber.xml的話,會(huì)new GenericApplicationContext
applicationContext = new GenericApplicationContext();
}
applicationContext.registerShutdownHook();
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
beanFactory.registerScope(GlueCodeScope.NAME, new GlueCodeScope());
for (Class<?> stepClass : stepClasses) {
registerStepClassBeanDefinition(beanFactory, stepClass);
}
return beanFactory;
}
最后,來(lái)說下GenericApplicationContext這個(gè)類,該類會(huì)根據(jù)Bean的Type類型,然后newInstance實(shí)例,但是由于這個(gè)類中又注入了其他的類,而注入的類是無(wú)法通過new實(shí)例的方式來(lái)初始化的,所以最后就會(huì)注入失敗,報(bào)空指針了。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Nacos配置中心搭建及動(dòng)態(tài)刷新配置及踩坑記錄
這篇文章主要介紹了Nacos配置中心搭建及動(dòng)態(tài)刷新配置及踩坑記錄,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
Java類初始化和實(shí)例化中的2個(gè)“雷區(qū)”
這篇文章主要介紹了Java類初始化和實(shí)例化中的2個(gè)“雷區(qū)”,大家要注意,感興趣的小伙伴們可以參考一下2016-02-02
Spring Cloud Alibaba使用Sentinel實(shí)現(xiàn)接口限流
這篇文章主要介紹了Spring Cloud Alibaba使用Sentinel實(shí)現(xiàn)接口限流,本文詳細(xì)的介紹了Sentinel組件的用法以及接口限流,感興趣的可以了解一下2019-04-04
聊聊Spring AOP @Before @Around @After等advice的執(zhí)行順序
這篇文章主要介紹了聊聊Spring AOP @Before @Around @After等advice的執(zhí)行順序,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2021-02-02
Java異步調(diào)用轉(zhuǎn)同步方法實(shí)例詳解
這篇文章主要介紹了Java異步調(diào)用轉(zhuǎn)同步方法實(shí)例詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06

