文章目录

  • 1 JUnit5
    • 1.1 基本概念
    • 1.2 Annotations
    • 1.3 Maven
  • 2 JMockit
    • 2.1 基本概念
    • 2.2 JMockit 架构

参考资料:
https://sjyuan.cc/junit5/user-guide-cn/
http://jmockit.cn/index.htm

1 JUnit5

1.1 基本概念

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

依赖关系图:

TaskEngine:
maven-surefire-plugin:

  • JUnit Platform

    • junit-platform-commons
    • junit-platform-console
    • junit-platform-console-standalone
    • junit-platform-engine
    • junit-platform-launcher
  • JUnit Jupiter:
    • junit-jupiter-api
    • junit-jupiter-engine:默认的测试引擎
    • junit-jupiter-params:支持JUnit Jupiter中的 参数化测试。
  • Junit Vintage
    • junit-vintage-engine

简而言之,JUnit Platform 提供了 JUnit5 框架相关的组件,包括框架启动,还有 TaskEngine 服务接口,谁会用到它呢?引擎开发人员以及构建工具和IDE提供商的开发人员!比如 JMockit 为了集成 JUnit5 开发了 JMockitTestEngine,Maven 测试相关插件 maven-surefire-plugin, 2.22.0 及以上版本原生支持 JUnit 5。

JUnit Jupiter 是面向测试开发人员和扩展开发人员,。

JUnit Vintage 提供了一个TestEngine,用于运行基于JUnit 3和JUnit 4的测试。

  • Test Method: 添加 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory, or @TestTemplate 注解的方法;
  • Test Class:包含 Test Method 的 Class;
  • @DisplayName: 定义 Test method 或 Test Class 的显示名称,运行或报告中使用;

Record-Replay-Verification
Arrange-Action-Assert

1.2 Annotations

@Test, @ParameterizedTest, @RepeatedTest, @TestFactory, @TestInstance, @TestTemplate, @DisplayName, @BeforeEach, @AfterEach, @BeforeAll, @AfterAll, @Nested, @Tag, @Disabled, @ExtendWith

@DisplayName("A special test case")
class StandardTests {@BeforeAllstatic void initAll() {}@BeforeEachvoid init() {}@Test@Tag("model")@DisplayName("first case")@EnabledOnJre(JAVA_8)@EnabledIfSystemProperty(named = "os.arch", matches = ".*64.*")void succeedingTest() {}@Testvoid failingTest() {fail("a failing test");}@Test@Disabled("for demonstration purposes")void skippedTest() {// not executed}@RepeatedTest(value = 1, name = "{displayName} {currentRepetition}/{totalRepetitions}")@DisplayName("Repeat!")void customDisplayName(TestInfo testInfo) {assertEquals(testInfo.getDisplayName(), "Repeat! 1/1");}@ParameterizedTest@ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" })void palindromes(String candidate) {assertTrue(isPalindrome(candidate));}@TestFactoryIterable<DynamicTest> dynamicTestsFromIterable() {return Arrays.asList(dynamicTest("3rd dynamic test", () -> assertTrue(true)),dynamicTest("4th dynamic test", () -> assertEquals(4, 2 * 2)));}@Nestedclass NestedTest{@Testvoid isEmpty() {assertTrue(true);}}@AfterEachvoid tearDown() {}@AfterAllstatic void tearDownAll() {}
}
  • @BeforeAll, @AfterAll 作用在静态方法上;
  • @Disabled: 禁止执行;
  • @EnabledOnJre, @EnabledOnOs, @EnabledIfSystemProperty, @EnabledIfEnvironmentVariable, @EnabledIf: 配置条件执行属性;
  • @Tag: 测试类和测试方法可以被@Tag注解标记。那些标记可以在后面被用来过滤测试发现和执行;
  • @Nested: 嵌套测试让测试编写者能够表示出几组测试用例之间的关系;
  • TestInfo, RepetitionInfo, TestReporter: 允许给测试类的构造函数和方法传入参数;
  • @RepeatedTest: 注解并指定重复运行一个测试方法的次数;
  • @ParameterizedTest: 参数化测试可以用不同的参数多次运行试; 使用 @ValueSource, @CsvSource, @CsvFileSource, @EnumSource, @MethodSource, @ArgumentsSource 来指定数据源;
  • @TestFactory: 方法本身不是测试用例,而是测试用例的工厂; DynamicTest是运行时生成的测试用例。它由一个显示名称 和Executable组成;

1.3 Maven

<build><plugins><plugin><artifactId>maven-surefire-plugin</artifactId><version>2.22.0</version><configuration><includes><include>Sample.java</include><include>%regex[.*(Cat|Dog).*Test.*]</include></includes><excludes><exclude>**/TestCircle.java</exclude><exclude>**/TestSquare.java</exclude></excludes><groups>acceptance | !feature-a</groups><excludedGroups>integration, regression</excludedGroups><properties><configurationParameters>junit.jupiter.conditions.deactivate = *junit.jupiter.extensions.autodetection.enabled = truejunit.jupiter.testinstance.lifecycle.default = per_class</configurationParameters></properties></configuration></plugin></plugins>
</build><dependencies><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.3.0</version><scope>test</scope></dependency><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-engine </artifactId><version>5.3.0</version><scope>test</scope></dependency>
</dependencies>

https://maven.apache.org/surefire/maven-surefire-plugin/examples/inclusion-exclusion.html

  • 按Tag过滤
  • 按测试类名过滤
  • 配置参数

2 JMockit

JMockit 中文网 http://jmockit.cn/index.htm

JMockit是一款Java类/接口/对象的Mock工具,目前广泛应用于Java应用程序的单元测试中。

2.1 基本概念

//JMockit的程序结构
public class ProgramConstructureTest {// 这是一个测试属性@MockedHelloJMockit helloJMockit;@Testpublic void test1() {// 录制(Record)new Expectations() {{helloJMockit.sayHello();// 期待上述调用的返回是"hello,david",而不是返回"hello,JMockit"result = "hello,david";}};// 重放(Replay)String msg = helloJMockit.sayHello();Assert.assertTrue(msg.equals("hello,david"));// 验证(Verification)new Verifications() {{helloJMockit.sayHello();times = 1;}};}@Testpublic void testInjectable(@Injectable Locale locale) {new Expectations() {// 这是一个Expectations匿名内部类{// 这是这个内部类的初始化代码块,我们在这里写录制脚本,脚本的格式要遵循下面的约定//方法调用(可是类的静态方法调用,也可以是对象的非静态方法调用)//result赋值要紧跟在方法调用后面//...其它准备录制脚本的代码//方法调用//result赋值}};// 静态方法不mockAssert.assertTrue(Locale.getDefault() != null);// 非静态方法(返回类型为String)也不起作用了,返回了null,但仅仅限于locale这个对象Assert.assertTrue(locale.getCountry() == null);// 自已new一个,并不受影响Locale chinaLocale = new Locale("zh", "CN");Assert.assertTrue(chinaLocale.getCountry().equals("CN"));}@Testpublic void testCaputring(@Capturing IPrivilege privilegeManager) {// 加上了JMockit的API @Capturing,// JMockit会帮我们实例化这个对象,它除了具有@Mocked的特点,还能影响它的子类/实现类new Expectations() {{// 对IPrivilege的所有实现类录制,假设测试用户有权限privilegeManager.isAllow(testUserId);result = true;}};// 不管权限校验的实现类是哪个,这个测试用户都有权限Assert.assertTrue(privilegeManager1.isAllow(testUserId));Assert.assertTrue(privilegeManager2.isAllow(testUserId));}@Testpublic void testMockUp() {// 对Java自带类Calendar的get方法进行定制// 只需要把Calendar类传入MockUp类的构造函数即可new MockUp<Calendar>(Calendar.class) {// 想Mock哪个方法,就给哪个方法加上@Mock, 没有@Mock的方法,不受影响@Mockpublic int get(int unit) {if (unit == Calendar.YEAR) {return 2017;}if (unit == Calendar.MONDAY) {return 12;}if (unit == Calendar.DAY_OF_MONTH) {return 25;}if (unit == Calendar.HOUR_OF_DAY) {return 7;}return 0;}};// 从此Calendar的get方法,就沿用你定制过的逻辑,而不是它原先的逻辑。Calendar cal = Calendar.getInstance(Locale.FRANCE);Assert.assertTrue(cal.get(Calendar.YEAR) == 2017);Assert.assertTrue(cal.get(Calendar.MONDAY) == 12);Assert.assertTrue(cal.get(Calendar.DAY_OF_MONTH) == 25);Assert.assertTrue(cal.get(Calendar.HOUR_OF_DAY) == 7);// Calendar的其它方法,不受影响Assert.assertTrue((cal.getFirstDayOfWeek() == Calendar.MONDAY));}@Testpublic void testClassMockingByExpectation() {AnOrdinaryClass instanceToRecord = new AnOrdinaryClass();new Expectations(AnOrdinaryClass.class) {{// mock静态方法AnOrdinaryClass.staticMethod();result = 10;// mock普通方法instanceToRecord.ordinaryMethod();result = 20;// mock final方法instanceToRecord.finalMethod();result = 30;// native, private方法无法用Expectations来Mock}};AnOrdinaryClass instance = new AnOrdinaryClass();Assert.assertTrue(AnOrdinaryClass.staticMethod() == 10);Assert.assertTrue(instance.ordinaryMethod() == 20);Assert.assertTrue(instance.finalMethod() == 30);// 用Expectations无法mock native方法Assert.assertTrue(instance.navtiveMethod() == 4);// 用Expectations无法mock private方法Assert.assertTrue(instance.callPrivateMethod() == 5);}
}

JMockit的程序结构:

  • 测试属性&测试参数:测试属性即测试类的一个属性。它作用于测试类的所有测试方法;测试参数即测试方法的参数。二者皆可以用 @Mocked, @Tested, @Injectable,@Capturing 来标注。测试参数与测试属性的不同,主要是作用域的不同。
  • Record-Replay-Verification: 与JUnit程序的AAA(Arrange-Action-Assert)结构是一样的。Record对应Arrange,先准备一些测试数据,测试依赖。Replay对应Action,即执行测试逻辑。Verification对应Assert,即做测试验证。
  • @Mocked: @Mocked修饰的类/接口,是让 JMockit 生成一个 Mocked 对象,这个对象方法(包含静态方法)返回默认值。如果返回类型是其它引用类型,则返回这个引用类型的Mocked对象。
  • @Tested, @Injectable: @Tested修饰的类,表示是测试对象; @Injectable 也表示一个Mocked对象,相比@Mocked,只不过只影响类的一个实例。
  • @Capturing: 主要用于子类/实现类的Mock;
  • Expectations: 主要是用于录制, 即录制类/对象的调用,返回值是什么。
  • @Mock: 直接 mock 对象的方法;
  • 用Expectations来Mock类与用Expectations来Mock实例的唯一不同就在于,前者影响类的所有实例,而后者只影响某一个实例。

2.2 JMockit 架构

架构图:

通过上面的架构图,我们可以看到JMockit有如下核心组件

  1. JVM Attach

JMockit使用了JDK6动态添加代理功能。目的是为了运行JMockit启动程序做准备。 JMockit提供了不同OS的hotSpot JVM的Attach支持: BsdVirtualMachine, LinuxVirtualMachine,SolarisVirtualMachine,WindowsVirtualMachine。

JMockit启动程序:主要功能是集成测试框架(JUnit/TestNG),完成对JMockit类转换器织入。

  1. 测试框架集成

提供了JUnit4/5, TestNG的支持。

a) 对JUnit4的集成方法:改写JUnit4的核心类org.junit.runner.Runner,org.junit.runners.model.FrameworkMethod, org.junit.runners.model.TestRunnerDecorator,org.junit.runners.model.RunNotifier。改写的目的是为了让测试程序在运行测试方法前,完成Mock 注解API(@Mocked,@Injectable,@Capturing)修饰的测试属性&测试参数的类做相关字节码的织入。
详见可以见JMockit源代码中Runner类,FakeFrameworkMethod类,JUnit4TestRunnerDecorator类,RunNotifierDecorator类。

b) 对JUnit5/TestNG的集成方法: 由于JUnit5/TestNG支持ServiceLoader的扩展体系,JMockit通过配置/META-INF/services/org.junit.platform.engine.TestEngine,/META-INF/services/org.testng.ITestNGListener完成对JUnit5/TestNG的集成。集成的目的同样是为了让测试程序在运行测试方法前,完成Mock 注解API(@Mocked,@Injectable,@Capturing)修饰的测试属性&测试参数的类做相关字节码的织入。

  1. 字节码处理

通过ASM,在类的某个方法中加入某段逻辑以达到Mock的目的;生成某个类的子类以支持抽象类的Mock;生成某个接口的实例类以支持接口的Mock。通过ASM, 这些都变得不那么复杂了。

  1. 类转换器

类转换器是JMockit的核心。Mock的核心就是JMockit不同的类转换器在起作用。

a)录制(ExpectationsTransformer):用于对new Expectations(){{}},new Verifications(){{}},匿名类进行重定义。用于支持测试程序中的录制,重放,校验。
b)伪类(ClassLoadingBridgeFields): 伪类,即new MockUp {}的匿名类或 extends MockUp的子类。用于伪类的@Mock方法提供支持。 通过识别伪类@Mock方法,在对应的方法体中织入一段分支,用于走伪类的@Mock方法逻辑。
c)覆盖率(CodeCoverage):用于支持JMockit Coverage功能。 通过在类的方法体行加埋点。即可以完成行覆盖率,路径覆盖率的计算。
d)类缓存(CachedClassfiles): 这个没有什么好说的,对类进行了重定义,当然要求一个测试方法结束后,能复原类的原有字节码,于是需要一个Cache了。
e)对象捕捉(CaptureTransformer): 用于支持JMockit的withCapture()功能,即捕捉某次测试中,某个类的某个方法的入参是什么,并记录下来。通常用于在验证代码块中,某个方法的入参是否符合期望。

  1. Mock API

@Mocked, @Tested ,@Injectable, @Capturing, MockUp, @Mock ,Expectations, Verifications这些API,通过前面基础知识,常见用法等的学习,这些API已经耳熟能详了吧。 基本能满足大部分的Mock场景了。

JUnit5 + JMockit 知识整理相关推荐

  1. 单元测试实践思考(junit5+jmockit+testcontainer)

    文章目录 背景 方案设计 单元测试指导思想 单层隔离 内部穿透 技术实现 依赖管理 基础架构 封装Junit5&Jmockit 单元测试配置 TestContainer封装 官方方案 实际方案 ...

  2. python常用变量名_python基础知识整理

    Python Python开发 Python语言 python基础知识整理 序言:本文简单介绍python基础知识的一些重要知识点,用于总结复习,每个知识点的具体用法会在后面的博客中一一补充程序: 一 ...

  3. Spring AOP 知识整理

    为什么80%的码农都做不了架构师?>>>    AOP知识整理 面向切面编程(AOP)通过提供另外一种思考程序结构的途经来弥补面向对象编程(OOP)的不足.在OOP中模块化的关键单元 ...

  4. Linux系统基础知识整理

    一.说明 本篇文章,我将结合自己的实践以及简介,来对linux系统做一个直观清晰的介绍,使得哪些刚接触Linux的小伙伴可以快速入门,也方便自己以后进行复习查阅. 二.基本知识整理 1.Linux文件 ...

  5. 计算机二级c语基础知识,计算机二级C语基础知识整理.doc

    计算机二级C语基础知识整理 1.1 算法 算法:是一组有穷指令集,是解题方案的准确而完整的描述.通俗地说,算法就是计算机解题的过程.算法不等于程序,也不等于计算方法,程序的编制不可能优于算法的设计. ...

  6. js事件(Event)知识整理

    鼠标事件 鼠标移动到目标元素上的那一刻,首先触发mouseover  之后如果光标继续在元素上移动,则不断触发mousemove  如果按下鼠标上的设备(左键,右键,滚轮--),则触发mousedow ...

  7. Spring学习篇:IoC知识整理(一)

    现在正通过spring的官方文档学习spring,将自己学习时的点点滴滴记录下来. Ioc知识整理(一): IoC (Inversion of Control) 控制反转. 1.bean的别名 我们每 ...

  8. 使用Aspose.Cells的基础知识整理

    使用Aspose.Cells的基础知识整理 转自 http://www.cnblogs.com/kenblove/archive/2009/01/07/1371104.html 这两天用Aspose. ...

  9. 前端基础知识整理汇总(中)

    前端基础知识整理汇总(中) Call, bind, apply实现 // call Function.prototype.myCall = function (context) {context = ...

  10. 前端基础知识整理汇总(上)

    前端基础知识整理汇总(上) HTML页面的生命周期 HTML页面的生命周期有以下三个重要事件: 1.DOMContentLoaded -- 浏览器已经完全加载了 HTML,DOM 树已经构建完毕,但是 ...

最新文章

  1. mysql winxp 本地 10061 错误
  2. 第三十一篇:SOUI布局之相对于特定兄弟窗口
  3. 深入理解分布式技术 - 消息幂等性如何保障不重复消费
  4. Hadoop每日一讨论整理版
  5. Springboot搭建个人博客系列
  6. LeetCode——排序
  7. Python(8):模块内置变量
  8. Yarn application has already ended! It might have been killed or unable to launch application master
  9. 量子计算机到底多强大?从量子运算看清楚它们的能力
  10. 如何使用JavaScript检查输入是否为空
  11. 第七天Python学习记录
  12. 多种语言签名代码实现
  13. SM3算法对大文件做摘要
  14. 人人商城删除后台菜单“小程序”
  15. DS18B20驱动详解(蓝桥杯单片机)
  16. 自家主机建云服务器_是用云主机还是自己建服务器好?
  17. Maven Failsafe 插件
  18. 学python看小甲鱼还是黑马_为什么我看完小甲鱼的python视频还是不会写呢?
  19. 预约制成为汽车年检新常态
  20. 2021不堪回首,2022满路荆棘,但依然乐观努力

热门文章

  1. 做一个微信欢乐斗地主之残局解答器!
  2. android oppo 驱动,OPPO R9驱动安装不了怎么办 OPPO R9驱动安装不了的解决方法
  3. 博图如何上载wincc程序_博图导入触摸屏程序 如何通过博图下载触摸屏程序
  4. (附源码)小程序 记账微信小程序 毕业设计 180815
  5. 一个简单的例子开启Spark机器学习
  6. windows开机启动方法
  7. GoDot引擎打包安卓
  8. Kotlin教程,从入门到精通
  9. Python语言程序设计 第七周 文件和数据格式化
  10. 矩阵快速幂 算法原理