1、新建一个springboot工程工程结构如下:
2、添加pom依赖
4.0.0
com.chhliu
spring-boot-cucumber
0.0.1-SNAPSHOT
jar
spring-boot-cucumber
Demo project for Spring Boot and Cucumber
org.springframework.boot
spring-boot-starter-parent
1.5.6.RELEASE
1.2.4
UTF-8
UTF-8
1.7
org.springframework.boot
spring-boot-starter-web
info.cukes
cucumber-java
${cucumber.version}
info.cukes
cucumber-core
${cucumber.version}
info.cukes
cucumber-spring
${cucumber.version}
info.cukes
cucumber-junit
${cucumber.version}
junit
junit
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
1.7
org.codehaus.mojo
exec-maven-plugin
1.7
integration-test
java
test
com.chhliu.test.CucumberTest.java
--plugin
pretty
--glue
src/test/resources/
2、编写service接口及其实现类
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"));
}
}
#language: zh-CN
#"zh-CN": {
# "but": "*|但是<",
# "and": "*|而且<|并且<|同时<",
# "then": "*|那么<",
# "when": "*|当<",
# "name": "Chinese simplified",
# "native": "简体中文",
# "feature": "功能",
# "background": "背景",
# "scenario": "场景|剧本",
# "scenario_outline": "场景大纲|剧本大纲",
# "examples": "例子",
# "given": "*|假如<|假设<|假定<"
# }
@bank
功能:假如我在银行取钱的时候,如果我登录成功并且输入的密码正确,那么会显示我的银行卡余额,假如余额为50万
场景:银行取钱
假如:我以"chhliu"登录
并且:输入的密码为"123456"
当:确认密码也为"123456"时
那么:显示银行卡余额为"500000"
package com.chhliu.test;
import org.junit.runner.RunWith;
import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;
/**
* @RunWith(Cucumber.class) 这是一个运行器 ,指用Cucumber来运行测试
* @CucumberOptions中的features,用于指定我们项目中要运行的feature的目录
* @CucumberOptions中的format,用于指定我们项目中要运行时生成的报告,并指定之后可以在target目录中找到对应的测试报告
* @CucumberOptions中的glue,用于指定项目运行时查找实现step定义文件的目录
*
* 在实际项目中,随着项目的进行,一个测试工程可能由多个feature文件组成,并且每个feature文件中可能也是由多个scenario组成。默认情况下,
* 每次运行是运行所有feature中的所有scenario。这样可能导致正常情况下运行一次测试脚本,需要非常长的时间来等待测试结果。
* 但是实际过程中,测试用例是有优先级等区分的。比如smokeTest、regressionTest等。或者有时候会有特别小部分的用例,比如等级是critical,
* 这些用例需要长时间运行来监测系统是否没有白页或者页面404等现象。
* 所以我们必须区分开所有的scenario,可以使我们在启动测试脚本时,可以根据我们需要来运行哪些模块的scenaro。这时我们可以使用Tags
* 在Cucumber里Tag是直接在Feature、Scenari或Scenario Outline关键字前给feature或scenario添加任意数量的前缀为@的tags,多个tag用空格来分隔
* @author chhliu
*
*/
@RunWith(Cucumber.class)
@CucumberOptions(plugin = {"json:target/cucumber.json", "pretty"}, features = "src/test/resources")
public class CucumberTest {
}
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.当;
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;
}
@当("^:确认密码也为\"([^\"]*)\"时$")
public void 确认密码也为_时(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);
}
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration // 不加此注解,bean会注入不进去
@SpringBootTest // 不加此注解会找不到bean
public class Cucumber集成spring{
}
2 Scenarios (2 passed)
11 Steps (11 passed)
0m0.091s
spring boot与Cucumber整合的时候,有个地方需要注意,因为spring boot提倡去xml化,所以传统方式下,Cucumber会读取classpath下的cucumber.xml配置文件来初始化bean的方式,和spring整合后,就不能用这种方式了,需要使用@ContextConfiguration注解来实现类的加载,如果是需要加载配置文件的方式的话,可以如下使用:
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
@ContextConfiguration(classes=SpringBootCucumberApplication.class)
或者直接
@ContextConfiguration
特别注意:@ContextConfiguration注解必加,否则会出现bean注入失败
下面,我们从源码来看下为什么会造成这种情况。
该部分涉及的代码都在cucumber-spring包下的SpringFactory类中,重点我们看下下面这个类:
public void start() {// cucumber测试启动方法
if (stepClassWithSpringContext != null) {// 如果使用了@ContextConfiguration注解的话,此处不为null
testContextManager = new CucumberTestContextManager(stepClassWithSpringContext);
} else {// 否则stepClassWithSpringContext就为null,会进入下面这个分支
if (beanFactory == null) {
beanFactory = createFallbackContext();// 这个方法是我们要跟的重点
}
}
notifyContextManagerAboutTestClassStarted();
if (beanFactory == null || isNewContextCreated()) {
beanFactory = testContextManager.getBeanFactory();
for (Class> stepClass : stepClasses) {
registerStepClassBeanDefinition(beanFactory, stepClass);
}
}
GlueCodeContext.INSTANCE.start();
}
private ConfigurableListableBeanFactory createFallbackContext() {
ConfigurableApplicationContext applicationContext;
if (getClass().getClassLoader().getResource("cucumber.xml") != null) {// 会先根据classpath下的cucumber.xml来初始化ConfigurableApplicationContext
applicationContext = new ClassPathXmlApplicationContext("cucumber.xml");
} else {// 如果没有配置cucumber.xml的话,会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;
}