惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Application and Cybersecurity Blog
Application and Cybersecurity Blog
The Register - Security
The Register - Security
V
Visual Studio Blog
aimingoo的专栏
aimingoo的专栏
Stack Overflow Blog
Stack Overflow Blog
IT之家
IT之家
量子位
C
Check Point Blog
博客园 - 【当耐特】
小众软件
小众软件
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
雷峰网
雷峰网
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Microsoft Azure Blog
Microsoft Azure Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Engineering at Meta
Engineering at Meta
Recorded Future
Recorded Future
The Last Watchdog
The Last Watchdog
博客园 - Franky
N
Netflix TechBlog - Medium
Webroot Blog
Webroot Blog
A
About on SuperTechFans
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
W
WeLiveSecurity
D
Docker
S
Security Affairs
T
The Blog of Author Tim Ferriss
F
Fortinet All Blogs
Blog — PlanetScale
Blog — PlanetScale
V2EX - 技术
V2EX - 技术
Jina AI
Jina AI
Help Net Security
Help Net Security
L
LangChain Blog
P
Proofpoint News Feed
The Cloudflare Blog
WordPress大学
WordPress大学
Google DeepMind News
Google DeepMind News
Schneier on Security
Schneier on Security
Recent Announcements
Recent Announcements
Attack and Defense Labs
Attack and Defense Labs
云风的 BLOG
云风的 BLOG
V
Vulnerabilities – Threatpost
Microsoft Security Blog
Microsoft Security Blog
H
Heimdal Security Blog
P
Proofpoint News Feed
O
OpenAI News
H
Help Net Security
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
爱范儿
爱范儿
Security Archives - TechRepublic
Security Archives - TechRepublic

浮华生

Elasticsearch 检索性能优化 - 浮华生 舆情监控系统综述 - 浮华生 2024 半年度总结 - 浮华生 2023 年终总结 - 浮华生 异地机器组网方案 - 浮华生 Kubernetes 部署 Elasticsearch 和 Kibana - 浮华生 2022 年终总结 - 浮华生 RabbitMQ connection channel 的关系 - 浮华生 Kafka Java 客户端 Producer 原理分析 - 浮华生 RabbitMQ 和 Kafka 应用原理简单对比 - 浮华生 阿里云 OpenSearch 介绍 - 浮华生 Golang Array 和 Slice 区别 - 浮华生 Mac OS 下打造 golang nvim 编程环境之基础配置 - 浮华生 电商搜索技术总结 - 浮华生 电商搜索业务总结 - 浮华生 2021 年终总结 - 浮华生 Cypress 实践总结 - 浮华生 年终总结 - 浮华生 关于我 - 浮华生 微服务应用集成 SpringCloud 步骤 - 浮华生 电商搜索数据同步方案 - 浮华生 通过一道数值转换题重温计算机补码 - 浮华生 macOS 系统推荐的一些软件 - 浮华生 DevOps 实施规划(持续更新) - 浮华生 rabbitmq 如何提高可靠性并保证消费端幂等 - 浮华生 AMQ Model总结 - 浮华生 结对编程 - 浮华生 RSocket 介绍 - 浮华生 面向对象的理解 - 浮华生 企业平台技术框架 - 浮华生 对创业的思考 - 浮华生 知难行易 - 浮华生 一年工作经验总结 - 浮华生 我与领域驱动之缘 - 浮华生 TDD 中使用的工具 - 浮华生 tf-idf 算法 - 浮华生 gitlab 添加代码规范检测 - 浮华生 query 改写 - 浮华生 阶段性总结 - 浮华生 操作系统作用 - 浮华生 - 浮华生 hamming-distance - 浮华生 ElasticSearch API 基本操作 - 浮华生 elasticsearch 集群容错 - 浮华生 ElasticSearch 基础概念 - 浮华生 技术选型怎么做 - 浮华生 条件概率、全概率与贝叶斯公式 - 浮华生 年终总结 - 浮华生 迁移到 ubuntu18 的问题及配置 - 浮华生 总结 - 浮华生 使用 psi-probe 监控 Tomcat - 浮华生 Tomcat 远程 Debug - 浮华生 jstack 死循环和死锁定位 - 浮华生 jmap & mat 内存溢出 - 浮华生 JVM 常用参数查看 - 浮华生 周总结(8.13-8.19) - 浮华生 周总结(7.30-8.4) - 浮华生 使用移位运算符 - 浮华生 master 公式 - 浮华生 VMware 12 NAT网络下配置 ubuntu 16.04 LTS 系统静态 IP - 浮华生 关于进制的计算 - 浮华生 项目总结 第三篇 - 浮华生 项目总结 第二篇 - 浮华生 editor.md 富文本编辑器的使用 - 浮华生 项目总结 第一篇 - 浮华生 2017至今总结 - 浮华生 谈谈微服务 - 浮华生 单例模式 - 浮华生 tor 使用 - 浮华生 归档 - 浮华生 搜索 - 浮华生 搜索 && 推荐 - 浮华生
使用 cucumber 进行行为驱动开发(BDD) - 浮华生
浮华生 · 2020-10-20 · via 浮华生

前言

保证软件质量的方式多种多样,根据测试金字塔上所描绘的,单元测试是最有效的,但是在公司内部实践的时候却遇到了一些问题,比如赶进度来不及写单元测试、改了代码逻辑单元测试也需要跟着变、验收时同样会有很多 bug,总结下来大概有几个原因:

  • 需求分析时有很多细节没有考虑到(这个不可避免)
  • 测试用例覆盖的场景不全
  • 分层架构需要对每一层都进行测试,会占用大量时间

还有一个原因是公司技术环境,我司是业务驱动的,主营业务在线下,最有价值是业务而非技术。

综合多方面的原因,决定尝试 BDD 的测试方式,这样做的好处可以分为以下几点:

  • 活文档,好的用例能够体现业务的整个面貌
  • 将关注点放到更有价值的业务上来,而非技术
  • 对于目前团队现状,BDD 的投入产出比要比单元测试更好

步骤

用户登录的用户故事举例:

作为一名供应商,我希望在输入用户名:”admin“,密码:“admin” 后能够进到系统首页,然后能够进行数据操作

验收标准:
 场景1:用户名密码正确
  Given:用户名:admin,密码:admin
  When:我在登录页上输入用户名 admin 和密码 admin
  Then:我将进入到系统首页
  
 场景2:用户名或密码不正确
 Given:用户名:admin,密码:admin
 When:我在登录页上输入用户名 admin 和密码 admin123
 Then:我被告知 “用户名或者密码错误”
 AND:此时我还是未登录状态
 

注意:对于用户故事验收标准这部分应该以用户行为为主,避免UI操作的字段出现,比如“我点击了提交按钮”,这样是不推荐的。

添加依赖

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-java</artifactId>
    <version>${cucumber.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-junit</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>

添加 feature 文件

在 /src/test/resource 文件夹下新建 feature 文件夹,并在此文件夹中创建 login.feature 文件。

添加一个场景

1
2
3
4
5
6
7
8
9
#language: zh-CN
功能供应商登录
   场景大纲: 用户名密码正确
     假设已注册的供应商用户名密码 admin admin
     当供应商输入用户名密码<username><password>
     那么能够进入到系统首页
     例子:
     |username|password|
     |admin   |admin   |

说明:

  • #language: zh-CN 指定使用的语言
  • 功能:对应 user story 的标题
  • 场景:对应功能的其中一种情况
  • 假设、当、那么:cucumber 指定的中文 DSL,对于 Given、When、Then
  • 对于要使用变量的地方使用 < > 标注出来并在下面的例子中列出数据内容

注意:feature 中的标点符号全部为英文标点

右键运行

此时我们还未创建任何业务代码,但是还是会有高亮展示

image-20201020175455431

此时,我们右键点击会有下面的按钮

image-20201020175654194

点击运行

image-20201020175752514

因为我们还没有编写代码,所以此时测试一定会报错,那么接下来需要做的是写代码让这段测试通过

步骤定义

在 /src/test/java 下新建一个 login 的文件夹,并新建 LoginStepDef java 文件。我们的测试逻辑将在这个文件中完成。本次仅对密码正确的情况做演示

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@CucumberContextConfiguration
public class LoginStepDef {

    private  Account account;
        @假设("^已注册的供应商用户名密码 admin admin$")
        public void setUp(){
            account = new Account("admin","admin");
        }

        @当("^供应商输入用户名密码(\\S*)(\\S*)$")
        public boolean when_input_account_info(String username,String password){
            if (account.valid(username,password)){
                return true;
            }
            return false;
        }
       @那么("^能够进入到系统首页$")
        public void get_into_index(){

       }

    }

说明:

  1. @CucumberContextConfiguration cucumber 提供的 Spring 集成注解

  2. @假设("^已注册的供应商用户名密码 admin admin$")@假设是中文的注解(中英文对照表请参见文末表格),与 feature 文件相对应。^ $ 和 shell 功能类似,代表一行的开头和结尾,固定格式。

  3. 对于需要传递参数的方法需要使用正则表达式进行占位

  • 整型 (\d+)

  • 字符串 (\S*)

  • 布尔值 (true|false)

  • 列表 使用 DataTable 举例:

    Given the following animals: | cow | | horse | | sheep |

    定义时:

    1
    2
    3
    
     @Given("the following animals:")
      public void the_following_animals(List<String> animals) {
      }
    

配置 Junit 引导

在 /src/test/java/login 中创建名为 LoginIntegerationTest 的 java 文件

1
2
3
4
5
6
@RunWith(Cucumber.class)
@CucumberOptions(features = "classpath:features/login",
        plugin = {"pretty", "html:target/cucumber/login.html"})
public class LoginIntegrationTest {
}

说明:

  1. @CucumberOptions cucumber 的配置注解
  2. features 指定 feature 文件的路径
  3. plugin 将测试结果以 html 的格式输出到指定目录

再次运行

image-20201021115138478

查看测试报告

根据配置的目录,找到对应的 html 文件

image-20201021115248832

Jenkins 集成

Github 中有开源的 Jenkins Plugin: Publish pretty cucumber-jvm reports on Jenkins

总结

如何选择合适的测试手段,需要结合企业研发团队的现状和经营模式做选型,TDD 虽然能提升质量,但并不适用所有的企业。

本文对 cucumber 做了一个基础的演示,对于复杂的环境,推荐结合 Rest Assured 对 API 做测试。

想要了解更多可以查看文末参考资料。

附表

中英文关键字的对照表

英文关键字中文关键字
feature“功能”
background“背景”
scenario“场景”, “剧本”
scenario_outline“场景大纲”, “剧本大纲”
examples“例子”
given“* “, “假如”, “假设”, “假定”
when“* “, “当”
then“* “, “那么”
and“* “, “而且”, “并且”, “同时”
but“* “, “但是”
given (code)“假如”, “假设”, “假定”
when (code)“当”
then (code)“那么”
and (code)“而且”, “并且”, “同时”
but (code)“但是”

参考资料

https://vitzhou.gitbooks.io/java-test-learning/content/cucumber/practice/chinese.html

https://vitzhou.gitbooks.io/java-test-learning/content/cucumber/practice/pass_parameters.html

https://developer.ibm.com/zh/articles/j-lo-cucumber01/

https://thepracticaldeveloper.com/cucumber-tests-spring-boot-dependency-injection/

相关书籍

Cucumber:行为驱动开发指南