开发者社区官方技术圈

阿里云开发者社区官方技术圈,用户产品功能发布、用户反馈收集等。

阿里云开发者社区官方技术圈,用户产品功能发布、用户反馈收集等。

在编写单元测试用例时,需要模拟各种依赖对象——类成员、方法参数和方法返回 值。

  1. 直接构建对象 如果需要构建一个对象,最简单直接的方法就是——定义对象并赋值。

  2. 反序列化对象 如果对象字段或层级非常庞大,采用直接构建对象方法,可能会编写大量构建程序 代码。这种情况,可以考虑反序列化对象,将会大大减少程序代码。由于 JSON 字 符串可读性高,这里就以 JSON 为例,介绍反序列化对象。 1) 反序列化模型对象 2) 反序列化集合对象 3) 反序列化映射对象

  3. 利用 Mockito.mock 方法 Mockito 提供一个 mock 功能,用于拦截那些尚未实现或不期望被真实调用的方法, 默认所有方法都已被模拟——方法为空并返回默认值(null 或 0),除非主动执行 doCallRealMethod 或 thenCallRealMethod 操作,才能够调用真实的方法。 利用 Mockito.mock 方法模拟依赖对象,主要用于以下几种情形: • 只使用类实例,不使用类属性; • 类属性太多,但使用其中少量属性(可以 mock 属性返回值); • 类是接口或虚基类,并不关心其具体实现类。

  4. 利用@Mock 注解 @Mock 注解跟 Mockito.mock 方法一样,可以用来模拟依赖对象,适用于普通类、 接口和虚基类。@Mock 注解需要配合@RunWith 注解使用。

  5. 利用 Mockito.spy 方法 Mockito.spy 方法跟 Mockito.mock 方法功能相似,只是 Mockito.spy 方法默认所有 方法都是真实方法,除非主动去模拟对应方法。

  6. 利用@Spy 注解 @Spy 注解跟 Mockito.spy 方法一样,可以用来模拟依赖对象,适用于普通类、接口 和虚基类。@Spy 注解需要配合@RunWith 注解使用。 注意: @Spy 注解对象需要初始化。如果是虚基类或接口,可以用 Mockito.mock 方法实例 化。

以上内容摘自《Java工程师必读手册》电子书,点击https://developer.aliyun.com/ebook/download/7780 可下载完整版

游客c3gxxcx6cqeyo 评论 0

1

回答

1) Mock 可以用来解除外部服务依赖,从而保证了测试用例的独立性。 现在的互联网软件系统,通常采用了分布式部署的微服务,为了单元测试某一服务 而准备其它服务,存在极大的依耐性和不可行性。

2) Mock 可以减少全链路测试数据准备,从而提高了编写测试用例的速度。 传统的集成测试,需要准备全链路的测试数据,可能某些环节并不是你所熟悉的。 最后,耗费了大量的时间和经历,并不一定得到你想要的结果。现在的单元测试, 只需要模拟上游的输入数据,并验证给下游的输出数据,编写测试用例并进行测试 的速度可以提高很多倍。

3) Mock 可以模拟一些非正常的流程,从而保证了测试用例的代码覆盖率。 根据单元测试的 BCDE 原则,需要进行边界值测试(Border)和强制错误信息输入 (Error),这样有助于覆盖整个代码逻辑。在实际系统中,很难去构造这些边界值, 也能难去触发这些错误信息。而 Mock 从根本上解决了这个问题:想要什么样的边 界值,只需要进行 Mock;想要什么样的错误信息,也只需要进行 Mock。

4) Mock 可以不用加载项目环境配置,从而保证了测试用例的执行速度。 在进行集成测试时,我们需要加载项目的所有环境配置,启动项目依赖的所有服务 接口。往往执行一个测试用例,需要几分钟乃至几十分钟。采用 Mock 实现的测试 用例,不用加载项目环境配置,也不依赖其它服务接口,执行速度往往在几秒之内, 大大地提高了单元测试的执行速度。

以上内容摘自《Java工程师必读手册》电子书,点击https://developer.aliyun.com/ebook/download/7780 可下载完整版

游客c3gxxcx6cqeyo 评论 0

在编写单元测试时,首先需要定义被测对象,或直接初始化、或通过 Spy 包装…… 其实,就是把被测试服务类进行实例化。

  1. 直接构建对象 直接构建一个对象,总是简单又直接。

  2. 利用 Mockito.spy 方法 Mockito 提供一个 spy 功能,用于拦截那些尚未实现或不期望被真实调用的方法, 默认所有方法都是真实方法,除非主动去模拟对应方法。所以,利用 spy 功能来定 义被测对象,适合于需要模拟被测类自身方法的情况,适用于普通类、接口和虚基 类。

  3. 利用@Spy 注解 @Spy 注解跟 Mockito.spy 方法一样,可以用来定义被测对象,适合于需要模拟被测 类自身方法的情况,适用于普通类、接口和虚基类。@Spy 注解需要配合@RunWith 注解使用。 @RunWith(PowerMockRunner.class) public class UserServiceTest { @Spy private UserService userService = new UserService(); ... } 注意: @Spy 注解对象需要初始化。如果是虚基类或接口,可以用 Mockito.mock 方法实例 化。

  4. 利用@InjectMocks 注解 @InjectMocks 注解用来创建一个实例,并将其它对象(@Mock、@Spy 或直接定义 的对象)注入到该实例中。所以,@InjectMocks 注解本身就可以用来定义被测对象。 @InjectMocks 注解需要配合@RunWith 注解使用。

以上内容摘自《Java工程师必读手册》电子书,点击https://developer.aliyun.com/ebook/download/7780 可下载完整版

游客c3gxxcx6cqeyo 评论 0

1

回答

在模拟完依赖的参数和返回值后,就可以利用 Mockito 和 PowerMock 的功能,进 行依赖方法的模拟。如果依赖对象还有方法调用,还需要模拟这些依赖对象的方法。

  1. 根据返回模拟方法 1) 模拟无返回值方法 2) 模拟方法单个返回值 3) 模拟方法多个返回值 直接列举出多个返回值: 转化列表为多个返回值: 4) 模拟方法定制返回值 可利用 Answer 定制方法返回值: 5) 模拟方法抛出单个异常 指定单个异常类型: 指定单个异常对象: 6) 模拟方法抛出多个异常 指定多个异常类型: 指定多个异常对象: 7) 直接调用真实方法

  2. 根据参数模拟方法 Mockito 提供 do-when 语句和 when-then 语句模拟方法。 1) 模拟无参数方法 对于无参数的方法模拟: 2) 模拟指定参数方法 对于指定参数的方法模拟: 3) 模拟任意参数方法 在编写单元测试用例时,有时候并不关心传入参数的具体值,可以使用 Mockito 参 数匹配器的 any 方法。Mockito 提供了 anyInt、anyLong、anyString、anyList、 anySet、anyMap、any(Class clazz)等方法来表示任意值。 4) 模拟可空参数方法 Mockito 参数匹配器的 any 具体方法,并不能够匹配 null 对象。而 Mockito 提供一 个 nullable 方法,可以匹配包含 null 对象的任意对象。此外,Mockito.any()方法也 可以用来匹配可空参数。 5) 模拟必空参数方法 同样,如果要匹配 null 对象,可以使用 isNull 方法,或使用 eq(null)。 6) 模拟不同参数方法 Mockito 支持按不同的参数分别模拟同一方法。 注意: 如果一个参数满足多个模拟方法条件,会以最后一个模拟方法为准。 7) 模拟可变参数方法 对于一些变长度参数方法,可以按实际参数个数进行模拟: 也可以用 Mockito.any()模拟一个通用匹配方法: 注意: Mockito. any()并不等于 Mockito.any(Class type),前者可以匹配 null 和类型 T 的可变参数,后者只能匹配 T 必填参数。

  3. 模拟其它特殊方法 1) 模拟 final 方法 PowerMock 提供对 final 方法的模拟,方法跟模拟普通方法一样。但是,需要把对 应的模拟类添加到@PrepareForTest 注解中。 2) 模拟私有方法 PowerMock 提供提对私有方法的模拟,但是需要把私有方法所在的类放在 @PrepareForTest 注解中。 3) 模拟构造方法 PowerMock 提供 PowerMockito.whenNew 方法来模拟构造方法,但是需要把使用 构造方法的类放在@PrepareForTest 注解中。 4) 模拟静态方法 PowerMock 提供 PowerMockito.mockStatic 和 PowerMockito.spy 来模拟静态方法 类 , 然 后 就可以 模 拟静态 方法了。 同 样,需要把 对 应 的 模 拟 类添加 到 @PrepareForTest 注解中。 注意: 第一种方式不适用于 PowerMockito.spy 模拟的静态方法类。

以上内容摘自《Java工程师必读手册》电子书,点击https://developer.aliyun.com/ebook/download/7780 可下载完整版

游客c3gxxcx6cqeyo 评论 0

1

回答

1

回答

PowerMock 为了更好地支持 SpringMVC/SpringBoot 项目,提供了一系列的注解, 大大地简化了测试代码。

  1. @RunWith 注解 • @RunWith(PowerMockRunner.class) • 指定 JUnit 使用 PowerMock 框架中的单元测试运行器。

  2. @PrepareForTest 注解 • @PrepareForTest({ TargetClass.class }) • 当需要模拟 final 类、final 方法或静态方法时,需要添加@PrepareForTest 注 解,并指定方法所在的类。如果需要指定多个类,在{}中添加多个类并用逗号隔 开即可。

  3. @Mock 注解 • @Mock 注解创建了一个全部 Mock 的实例,所有属性和方法全被置空(0 或者 null)。

  4. @Spy 注解 • @Spy 注解创建了一个没有 Mock 的实例,所有成员方法都会按照原方法的逻辑 执行,直到被 Mock 返回某个具体的值为止。 注意: @Spy 注解的变量需要被初始化,否则执行时会抛出异常。

  5. @InjectMocks 注解 @InjectMocks 注解创建一个实例,这个实例可以调用真实代码的方法,其余用 @Mock 或@Spy 注解创建的实例将被注入到用该实例中。

  6. @Captor 注解 @Captor 注解在字段级别创建参数捕获器。但是,在测试方法启动前,必须调用 “MockitoAnnotations.openMocks(this)”进行初始化。

  7. @PowerMockIgnore 注解 为了解决使用 PowerMock 后,提示 ClassLoader 错误。

以上内容摘自《Java工程师必读手册》电子书,点击https://developer.aliyun.com/ebook/download/7780 可下载完整版

游客c3gxxcx6cqeyo 评论 0

1) 好的单元测试必须遵守 AIR 原则 单元测试在线上运行时,感觉像空气(AIR)一样感觉不到,但在测试质量的保障上, 却是非常关键的。好的单元测试宏观上来说,具有自动化、独立性、可重复执行的 特点。 • A:Automatic(自动化) • I:Independent(独立性) • R:Repeatable(可重复)

2) 单元测试应该是全自动执行的,并且非交互式的。 测试用例通常是被定期执行的,执行过程必须完全自动化才有意义。输出结果需要 人工检查的测试不是一个好的单元测试。单元测试中不准使用 System.out 来进行 人肉验证,必须使用 assert 来验证。

3) 单元测试是可以重复执行的,不能受到外界环境的影响。 单元测试通常会被放到持续集成中,每次有代码 check in 时单元测试都会被执行。 如果单测对外部环境(网络、服务、中间件等)有依赖,容易导致持续集成机制的 不可用。 正例: 为了不受外界环境影响,要求设计代码时就把 SUT 的依赖改成注入,在测试时用 spring 这样的 DI 框架注入一个本地(内存)实现或者 Mock 实现。

4) 编写单元测试代码遵守 BCDE 原则,以保证被测试模块的交付质量。 • B:Border,边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等。 • C:Correct,正确的输入,并得到预期的结果。 • D:Design,与设计文档相结合,来编写单元测试。 • E:Error,强制错误信息输入(如:非法数据、异常流程、业务允许外等),并 得到预期的结果。

以上内容摘自《Java工程师必读手册》电子书,点击https://developer.aliyun.com/ebook/download/7780 可下载完整版

游客c3gxxcx6cqeyo 评论 0

1) 利用数组实现 首先,在调用函数中,定义一个对象数组,把所有输入输出参数存入对象数组中; 其次,在被调用函数中,把这些参数从对象数组中取出来使用;再次,在被调用函数中,再把这些参数值存入对象数组中;最后,在调用函数中,把这些参数值从对象数组中取出来使用。利用对象数组的问题是——代码可读性太差,而且在参数的存入和取出过程中,需要进行数据类型的强制转化。如果所有输入输出参数的类型一致,可以直接定义该类型的数组,从而避免了数据类型的强制转化。 2) 利用 Map 实现 首先,在调用函数中,定义一个对象 Map,把所有输入输出参数存入对象 Map 中; 其次,在被调用函数中,把这些参数从对象 Map 中取出来使用;再次,在被调用函数中,再把这些参数值存入对象 Map 中;最后,在调用函数中,把这些参数值从对象 Map 中取出来使用。 利用对象 Map 实现,代码的可读性比利用对象数组实现更强,但是也存在同样的问题——在参数的存入和取出过程中,需要进行数据类型的强制转化。如果所有输入输出参数的类型一致,可以直接定义该类型的 Map,从而避免了数据类型的强制转化。但是,利用对象 Map 实现,还不如定义一个参数类更实用。 3) 利用原子类实现 JDK 中,提供了一套原子类——AtomicInteger、AtomicLong、AtomicDouble 等,可用于对应的基础类型和包装类型,实现对应参数的输入输出功能。实现方法跟 ObjectHolder 一样,这里不再累述。以上内容摘自《Java工程师必读手册》电子书,点击https://developer.aliyun.com/ebook/download/7780 可下载完整版

游客c3gxxcx6cqeyo 评论 0

公告

阿里云开发者社区官方技术圈,用户产品功能发布、用户反馈收集等。

展开