百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

有效的单元测试(有效的单元测试第四章)

toyiye 2024-09-03 22:32 3 浏览 0 评论

认识单元测试

什么是单元测试

测试有黑盒测试和白盒测试之分,黑盒测试顾名思义就是我们不了解盒子的内部结构,我们通过文档或者对该功能的理解,指定了相应的输入参数,然后判断得出的结果是否正确。普通的用户、

开发人员、QA工程师都可以进行黑盒测试。

与之对应的白盒测试则需要了解到内部的实现细节,一般是由开发人员自己来进行的,是基于对代码逻辑结构、各个关联方法了解上进行的。

白盒测试主要有2种类型

  • 静态代码分析:静态的分析代码质量,如用Findbugs插件扫描代码。
  • 动态测试:单元测试,用预先的一组数据动态的调用代码。

单元测试的必要性

在金字塔越下面的地方 bug 是最多、最好发现、修改成本最小的。反之逐渐往上 bug 的修复成本是越来越大的。直观的感受就是一个开发期的 bug,看一眼代码可能就知道问题了。如果是换成线上的,好一点的话通过日志可以判断出来,如果日志看了没发现什么那就有点惨了。有时候需要在测试环境模拟生产的场景进行排查等。

单元测试具有以下几个好处

  • 方便对局部代码逻辑进行验证(如:微服务架构的话 rpc 端的方法不好触发,不用启动整个系统)
  • 快速的反馈问题(将问题快速的扼杀在构建前)
  • 给代码重构提供了底气(复杂的模块经常不敢轻易变动,因为关联太多怕导致其它异常,有时候也经常发生在修补了一个bug引发了另一个bug的问题)

单元测试的基础知识

如何编写单元测试

可理解的代码非常重要,测试代码也是如此。建议测试代码遵循 BDD(Behavior Driven Development )的风格,即使代码再复杂你也要将它抽象出 //given //when //then 三个基础部分。

通过测试用例反思你的代码

  • given 定义了一大堆,就是在测试一个方法的时候需要 mock 一大堆对象,这时候可能是代码设计有误,内聚力不足、与外部高度耦合、职责模糊。
  • when 一般只有一个,如果需要调用多个的话,可能实现代码没有给使用者提供合适的接口,需要调用一大堆接口才能完成一个动作。
  • then 主要是要去校验结果,这边的校验要尽可能的包含所有出参结果的校验。不能只看调用后没报错,或者返回的状态码是对的就好了。

import static org.mockito.BDDMockito.*;

Seller seller = mock(Seller.class);

Shop shop = new Shop(seller);

public void shouldBuyBread() throws Exception {

//given

given(seller.askForBread()).willReturn(new Bread());

//when

Goods goods = shop.buyBread();

//then

assertThat(goods, containBread());

}

拓展:

  • TDD(测试驱动开发)这是极限编程中推崇的开发模式,要求在开发前先写测试代码,基于测试的代码来编写业务代码,这样的好处一方面可以反复检验你的功能代码,一方面有助于你业务代码具有高内聚低耦合的效果。
  • BDD(行为驱动开发)可以看作 TDD 的一个补充,它主要是输出一些更贴近用户的文档,让用户更好理解。 据我简单的理解是:基于BDD 的测试用例你要充分写好你的注解,given(说明好上下文),when(指定哪个事件),then(对结果进行较详细的说明)
  • DDD(领域驱动开发)他主要是对业务进行建模,是业务人员与开发人员沟通的桥梁,他要求开发人员在写代码前要充分的理解业务。现在微服务的架构很火热,微服务里面注重业务的划分,DDD 是微服务的一个不错的实践方式。

基础设施

单元测试一般可以借助自动化构建工具,每次打包的时候进行自动化测试,有问题直接抛出,保证了打包程序的质量,单元测试通过后可以自动发布到开发、QA、仿真等环境。 常见的自动化构建工具有:Jenkins、gitlab-ci、gitlab、docker

常见术语

  • stub

很多人把它叫做桩。 测试某个代码时可能需要访问外部系统的服务,这时候我们可以启一个模拟的服务来代替。(比如系统中需要对接各个快递公司的服务)。

  • mock

与stub类似,都是用来模拟一些外部依赖。stub主要指的是外部系统,mock一般是本系统内的其他方法。

  • spy

spy是间谍的意思,监视某个对象的行为。mock是模拟了整个对象,而spy的对象还是真实的对象,它可以对真实对象中的某些行为进行改变。

@Test

public void real_partial_mock(){

List list = spy(new ArrayList());

// 执行的是list的真实api

assertEquals(0,list.size());

A a = mock(A.class);

// 执行的所有方法都不是A的

when(a.doSomething(anyInt())).then(。。。);

}

单元测试实战

工具介绍

mocktio

单元测试中一个很好的测试工具,简单列举下它的功能点,详情点击:传送门

  • 可以mock对象 @Mock
  • 可以spy某个真实对象的个别方法 @spy
  • 可以方便的往某个对象中注入mock对象 @InjectMocks
  • 可以模拟调用方法时的参数匹配 when(mockedList.get(anyInt())).thenReturn("element");
  • 可以判断某个方法调用次数 verify(mockedList, times(1)).add("once");
  • 可以模拟方法调用抛出异常 when(mockedList.get(1)).thenThrow(new RuntimeException());

powermock/jmock

可对静态方法、私有函数、Final函数进行模拟

testNG

可以比较方便的进行集成测试

AssertJ

比junit强大很多的断言工具,能够很强大的进行流式断言。

传送门

Assertions.assertThat(propsSendDetailPoList)

.isNotEmpty()

.hasSize(3)

.extracting(PropsSendDetailPo::getFcalqty1)

.contains(0L,0L,2L);

场景分析

静态对象mock

参看

@RunWith(PowerMockRunner.class) //1

@PrepareForTest({WebChatUtil.class}) //2

public class WebChatUtilTestCase extends AbstractJUnit {

@Before

public void init(){

PowerMockito.mockStatic(WebChatUtil.class);// 3

}

@Test

public void testWebchatEnable(){

try {

Calendar c = Calendar.getInstance();

c.set(c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DATE), 16, 35);

PowerMockito.spy(WebChatUtil.class); // 创建spy,如果不创建的话,后面调用WebChatUtil就都是Mock类,这里创建了spy后,只有设置了mock的方法才会调用mock行为

PowerMockito.doReturn(c).when(WebChatUtil.class, "getCurrentTime"); //Mock私有方法

} catch (Exception e) {

e.printStackTrace();

}

}

}

跨类mock

你要测试的方法里面引用了别的对象的方法,对于这个待测方法里面依赖别的对象你获取不到,如何对他进行mock。

  • 最原始的方法-自己用反射实现 此时要测试 A 对象,A里面引用了 B 对象,用来进行持久化操作的,这时候可以通过反射,将 A 对象的 B 属性替换成自己定义的mock对象。 B bmapper = mock(B.class); // 一个待测试的对象 A a = new A(); // bPoMapper 是 A 里面的一个属性(处理持久化用的) Class<A> aclass = A.class; Field bMapperField = aclass.getDeclaredField("bmapper"); bMapperField.setAccessible(true); bMapperField.set(a, bmapper);
  • 借助mocktio工具 //@InjectMocks 会将所有 @mock对象注入到自己的属性中 @InjectMocks A a; @Mock B b;

常见问题

  • 很多人会在每个类中继承 springjunit 框架导致构建时候非常慢,每继承一个spring容器就会反复启动一次,严重占用了构建机器的性能也影响效率,我们只是为了测试代码逻辑没什么必要启动spring容器,依赖的 bean 可以手动 new 出来,可以自己 mock 相应对象。有的人启动 spring 容器是为了能够连接数据库可以写一个连接数据库的基类可以借助某些工具 dbunit 等
  • 测试数据依赖某个数据库测试对于的数据全部要自己初始化,测试用例跑完所有的数据必须回滚。可以用内存数据库可以在构建的时候专门指定一个测试数据库
  • 不要只是为了覆盖率而测试要对测试的场景足够清楚,对返回对断言足够细致,不然很难发现问题,就会导致一个现象 - 你的测试用例只是为了证明你对代码是对的,而不是在校验你对代码。

相关推荐

# Python 3 # Python 3字典Dictionary(1)

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中,格式如...

Python第八课:数据类型中的字典及其函数与方法

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值...

Python中字典详解(python 中字典)

字典是Python中使用键进行索引的重要数据结构。它们是无序的项序列(键值对),这意味着顺序不被保留。键是不可变的。与列表一样,字典的值可以保存异构数据,即整数、浮点、字符串、NaN、布尔值、列表、数...

Python3.9又更新了:dict内置新功能,正式版十月见面

机器之心报道参与:一鸣、JaminPython3.8的热乎劲还没过去,Python就又双叒叕要更新了。近日,3.9版本的第四个alpha版已经开源。从文档中,我们可以看到官方透露的对dic...

Python3 基本数据类型详解(python三种基本数据类型)

文章来源:加米谷大数据Python中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。在Python中,变量就是变量,它没有类型,我们所说的"类型"是变...

一文掌握Python的字典(python字典用法大全)

字典是Python中最强大、最灵活的内置数据结构之一。它们允许存储键值对,从而实现高效的数据检索、操作和组织。本文深入探讨了字典,涵盖了它们的创建、操作和高级用法,以帮助中级Python开发...

超级完整|Python字典详解(python字典的方法或操作)

一、字典概述01字典的格式Python字典是一种可变容器模型,且可存储任意类型对象,如字符串、数字、元组等其他容器模型。字典的每个键值key=>value对用冒号:分割,每个对之间用逗号,...

Python3.9版本新特性:字典合并操作的详细解读

处于测试阶段的Python3.9版本中有一个新特性:我们在使用Python字典时,将能够编写出更可读、更紧凑的代码啦!Python版本你现在使用哪种版本的Python?3.7分?3.5分?还是2.7...

python 自学,字典3(一些例子)(python字典有哪些基本操作)

例子11;如何批量复制字典里的内容2;如何批量修改字典的内容3;如何批量修改字典里某些指定的内容...

Python3.9中的字典合并和更新,几乎影响了所有Python程序员

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

Python3大字典:《Python3自学速查手册.pdf》限时下载中

最近有人会想了,2022了,想学Python晚不晚,学习python有前途吗?IT行业行业薪资高,发展前景好,是很多求职群里严重的香饽饽,而要进入这个高薪行业,也不是那么轻而易举的,拿信工专业的大学生...

python学习——字典(python字典基本操作)

字典Python的字典数据类型是基于hash散列算法实现的,采用键值对(key:value)的形式,根据key的值计算value的地址,具有非常快的查取和插入速度。但它是无序的,包含的元素个数不限,值...

324页清华教授撰写【Python 3 菜鸟查询手册】火了,小白入门字典

如何入门学习python...

Python3.9中的字典合并和更新,了解一下

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

python3基础之字典(python中字典的基本操作)

字典和列表一样,也是python内置的一种数据结构。字典的结构如下图:列表用中括号[]把元素包起来,而字典是用大括号{}把元素包起来,只不过字典的每一个元素都包含键和值两部分。键和值是一一对应的...

取消回复欢迎 发表评论:

请填写验证码