前脚研究完 Mockito 中被 Mocked 的对象属性及方法的默认值, 虽然目今更多的是拥抱着 Mockito, 但总有时对 JMockit 也会挤眉弄眼,谁叫 JMockit 无所不能呢!被 Mockito 的 Mock 对象方法的默认返回值洗脑之后,进而觉察出 JMockit 应该有同样的实现方式。
经过类似的测试,这里不详细列出测试过程,只是在基于前篇的测试中加入 JMockit 的依赖,最新版是 1.36。测试类 MyClassTest 中使用1@Mocked 2private MyClass myClass;
来构造 MyClass 的 mock 对象 myClass, 其余代码是一样的。相关代码请前往上篇 Mockito 中被 Mocked 的对象属性及方法的默认值 中找。 使用 JMockit 后跑出来的效果如下: Read More
在 Java 测试中使用 Mockito 有段时日了,以前只是想当然的认为 Mock 的对象属性值和方法返回值都是依据同样的规则。基本类型是 0, 0.0, 或 false, 对象类型都是 null, Mock 对象的默认返回值也应该是一样的。直到最近有一天,有一个返回
Optional<String>类型的方法,由于忘记对该方法打桩,意外的发现它返回的不是 null, 而Optional.empty(), 因此才意识到此处定有蹊跷。着实有必要用代码验证一下 Mockito 是怎么决定属性及方法的各种返回类型的默认值的。此次测试所用的 Mockito 版本是 mockito-core-2.12.0.
于是创建了下面一个类 MyClass 用于生成 Mock 对象,选取了一些典型的数据类型, 包括 int, Double, String, long[], Optional<String>, Collection<String>, Map<String, String>, 同时测试 Mock 对象默认的属性值与方法默认返回值。 Read More
- 昨天刚刚侍弄完 Spring 下基于自定义注解拦截方法调用, 现在试下纯 AspectJ 的方式来打造,因为不是每一个项目都是 Spring。这次要推到 5 年前试验过用 javac 命令行编译的方式织入方面, 见 AspectJ 基于自定义的方法注解来拦截方法, 这次着重在用 aspectj-maven-plugin 插件的方法来织入 AspectJ 方面。
基本上代码还是昨天的,需求还是一样的:
被 @LogStartTime 注解的方法在进入该方法时记录当前时间在 ThreadLocal 中,并能根据 @LogStartTime 的属性值决定处理逻辑
因为 Java5+ 之后 AspectJ 可以写成 Java 类加注解的方式,*.aj 文件一般都没太大必要了,所以可以和 Spring AOP 共用一个 @Aspect 注解的方面代码MethodStartAspect。
我们将采用编译器织入,因此项目依赖只需要一个org.aspectj:aspectjrt:1.8.0, 它也不会引入别的组件。同样我们从 Main 方法和测试用例两方面来验证实现的效果,下面是整个测试项目的布局,以及依赖,除掉单元测试的其时就只需要一个 jar 包。 Read More
上一篇 JUnit 5 快速上手(从 JUnit 4 到 JUnit 5) 介绍了如何在一个项目中同时使用 JUnit 4 和 JUnit 5。现在来开始了解 JUnit 5 的新特性. 我们现在的项目基本是用 Maven 来管理依赖,在 Maven 项目中如何引入 JUnit 5 可以参考官方例子 junit5-maven-consumer. 我们知道 JUnit 5 包括三个模块,不用 JUnit 4 的话只要 Platform 和 Jupiter, 而 Jupiter Maven 模块本身依赖于 JUnit Platform, 因此应用 JUnit 5 的项目 Maven 配置就是1<dependency> 2 <groupId>org.junit.jupiter</groupId> 3 <artifactId>junit-jupiter-engine</artifactId> 4 <version>5.0.0</version> 5 <scope>test</scope> 6</dependency>
这样在当前的 IntelliJ IDEA(2017.2.4) 可以执行 JUnit 5 的测试用例。但要让 Maven 找到 JUnit 5 的测试用例,还得在pom.xml中加上 Read More
在研究 JUnit 5 新特性的时候,学习到其中有一节 Test Instance Lifecycle, 才意识到对 JUnit 的理解一直存在一个误区,以为 JUnit 是以测试类为一个生命周期的,其实不然。不管是 JUnit 5 还是 JUnit 4 或更早的版本,JUnit 都是以测试方法为一个独立的生命周期。
只是到了 JUnit 5 提供了方法来把生命周期由方法改为测试类,对于单个测试类可以使用注解
@TestInstance(Lifecycle.PER_CLASS)来指定用一个测试实例来跑所有的测试方法,这就意味着测试类中的成员变量只被初始化一次。@TestInstance的 Lifecycle 默认是 PER_METHOD, JUnit 4 就是 PER_METHOD, 而且是不能改的。如果在 JUnit 5 中改变为 PER_CLASS, 恐怕反而会出许多乱子,每个测试方法本就该是完全独立的。比如在同一个类中多个测试方法使用了同一个实例变量的情况下,总会用一个
@After方法来复位该实例变量,现在才知道那是多余的。像下面的代码1public class CalculatorTest { 2 3 private int number = 100; 4 5 @Test 6 public void test1() { 7 System.out.println(number); 8 number = 200; 9 } 10 11 @Test 12 public void test2() { 13 System.out.println(number); 14 number = 300; 15 } 16}
一直在关注 JUnit 5 的演进,自两年前首个 ALPHA 版后,经历了 6 的 Milestone, 3 个 RC 终于在 2017/09/10 正式发布了。其实还从未对其深究过,今天算是正式开始体验。
不像以往的版本,JUnit 5 现在是三个模块的合体 JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
- JUnit Platform: 运行测试的基础平台。还定义了开发测试框架的 TestEngine API。并提供了命令行执行测试以及与 Gradle, Maven, JUnit4 Runner 的集成
- JUnit Jupiter: 包含了新的编程和扩展模型。它还提供了一个运行新型测试的 TestEngine 实现
- JUnit Vintage: 提供了一个让 JUnit Platform 运行 JUnit 3 和 JUnit 4 的 TestEngine 实现
以上三个模块分工还是很明确,因此
- 从现有的 JUnit 4 项目步入到 JUnit 5 至少两 JUnit Platform 和 JUnit Vintage 两个
- 建立全新项目可以只引入 JUnit Platform 和 JUnit Jupiter
- 混合型当然是三个全部引入
但是由于 jar 包之间本身存在某种依赖关系,所以实际上 pom.xml 可以比想像的更简单 Read More
- 两年前写过一篇 Java 反射修改 final 属性值, 在这里重新温习一下,假设有个类
class Person {
这里声明 name 为非静态的属性只是为了说明反射修改 final 属性无关乎静态不静态,静态只是表现在它是一个类属性,在一个类加载器空间只会有一份拷贝,仅此而已。
public final String name = "Mike";
}
创建一个通用方法进行反射修改属性值1public static void modify(Object object, String fieldName, Object newFieldValue) throws Exception { 2 Field field = object.getClass().getDeclaredField(fieldName); 3 4 Field modifiersField = Field.class.getDeclaredField("modifiers"); 5 modifiersField.setAccessible(true); //Field 的 modifiers 是私有的 6 modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 7 8 if(!field.isAccessible()) { 9 field.setAccessible(true); 10 } 11 12 field.set(object, newFieldValue); 13}
调用 modify(...) 方法试图修改 person 的 name 属性 Read More 我们可以用自定义的 URLClassLoader 从外部动态加载类,并使用它。但数据库驱动的管理类 DriverManager 却不比较苛刻,不承认非当前应用系统加载器加载的驱动类。见 DriverManager 的 JavaDoc
When the method
getConnectionis called, theDriverManagerwill attempt to locate a suitable driver from amongst those loaded at initialization and those loaded explicitly using the same classloader as the current applet or application对于有有应用自定义类加载器加载数据库驱动类的需求时,就要对原 Driver 简单包装一下。继续往后会说介绍为什么要这么做。
说明一下,DriverManager 能够根据 JDBC 连接字符串匹配到驱动类,所以一般来说都不需要显式调用 DriverManager.registerDriver() 方法。
先看 DriverManager 在应用外部驱动类时会出现什么情况 Read More
Apache Avro 是类似于 Google protobuf 那样的数据交换协议,但 Avro 可以用 JSON 格式来定义 Schema, 所以相比而言更容易上手。它也是 Hadoop, Kafka 所采用的交换格式。对于生成的 avro 序列化文件如果要编写代码来解读其中内容的话就太过于麻烦,Apache 给了我们一个便捷的工具来处理 Avro Schema 和数据。
Java 版的 Avro Tools 可点击链接 avro-tools-1.8.2.jar 下载,当前版本 1.8.2(发布于 2017/05/20),执行命令是java -jar avro-tools-1.8.2.jar ..............
如果是 Mac 平台,还可以通过brew install avro-tools
来安装,执行命令就只是avro-tools了。
在本文中还会用到一个 JSON 格式化高亮显示的工具jq, 在 Mac 下通过以下命令安装brew install jq
avro-tools 和 jq 已准备就绪,接下来演示下如何使用它们。
Read More
三个月前写过一篇 Mockito 中捕获 mock 对象方法的调用参数,一般项目中 Mockito 不决求助于 JMockit, 同样的在 JMockit 也需对捕获被 Mock 的方法调用参数。当我们用new Expectations(){{}}打桩并在后面断言了返回值,那就无需捕获参数来断言,匹配到了方法调用即证明传入的参数也是对的,如下面的代码所示1public class UserServiceTest { 2 3 @Mocked 4 private UserDao userDao; 5 6 @Test 7 public void couldCallUserDaoToAddUser() { 8 new Expectations(){{ 9 userDao.findById(123); 10 result = "Yanbin"; 11 }}; 12 13 UserService userService = new UserService(userDao); 14 String user = userService.findBy(123); 15 16 assertThat(user).isEqualTo("Yanbin"); //这里断言成功也就证明了 userDao.findById(123) 方法被调用,参数必须是 123 17 } 18}
但如果是未打桩的方法,或打桩是用的模糊参数(withInstanceOf(String.class)), 或是无返回值的方法就要事后对是否调用了某个方法以及传入什么参数的情况进行断言。 Read More