
一直在关注 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
关注 Kotlin 已有段时日了,真是因为 Google 把它扶正而跑来跟风。因为进行想在 Java 与 Scala 间找一个折中的编程语言, 也就是 Kotlin。这是一篇好几月前列的我 想像中理想编程语言的几个特征, 琢磨来去当今也就 Kotlin 比较符合我的口味。很早就想买 《Kotlin IN ACTION》这本书,因那是 Kotlin 1.1 刚出,而出版的书只涵盖到了 Kotlin, 所以未出手。 看看再有一本好的那样的书估计也不是一时半会儿,所以今天还是把那本书弄到手了,至于 Kotlin 1.1 后的特性自个去补充。
尽管书中未提及 Kotlin 语言的设计灵感来自于何种语言, 但我的直觉就是与 Scala 太多相似之处,但没有 Scala 简单,并揉合一些 Swift 的特性,因此我在阅读 《Kotlin IN ACTION》时更多的会和 Scala 相比较。
第一个主题是关于 Kotlin 函数的定义与约定。Kotlin 的基本定义格式与 Scala 是类似的//Kotlin
注:Kotlin 也像 Scala 一样,if 不再是一个控制语句,而是一个表达式,所以它是有返回值的。与 Java 有不同的是,Kotlin 的赋值语句是没有返回值的,不能用作 val b = (a = 3), 而 Scala 的赋值语句总是返回 Unit Read More
fun max(a: Int, b: Int): Int {
return if (a > b) a else b
}
三个月前写过一篇 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
Java 测试的 Mock 框架以前是用 JMockit, 最近用了一段时间的 Mockito, 除了它流畅的书写方式,经常这也 Mock 不了,那也 Mock 不了,需要迁就于测试来调整实现代码,使得实现极不优雅。比如 Mockito 在 私有方法,final 方法,静态方法,final 类,构造方法面前统统的缴械了。powermock 虽然可作 Mockito 的伴侣来突破 Mockito 本身的一些局限,但是我一用它来 Mock 一个构造方法就出错Caused by: java.lang.ClassNotFoundException: org.mockito.exceptions.Reporter
原因是 Mockito 变化太快,powermock 跟不上它的步伐 -- https://github.com/powermock/powermock/issues/684,于是我只能止步。
不得已再祭出 JMockit 这号称(也确实是)一无所不能的大杀器,在此见识一下它怎么 Mock 构造函数的
本篇实例所使用的 JMockit 版本是 1.30, 当前最新版 1.31, 由于尚未被 Maven 中央仓库收录,所以暂用 1.30。在 pom.xml 中如下方式引入 Read More
Spring 的 JdbcTemplate 为我们操作数据库提供非常大的便利,不需要显式的管理资源和处理异常。在我们进入到了 Java 8 后,JdbcTemplate 方法中的回调函数可以用 Lambda 表达式进行简化,而本文要说的正是这种 Lambda 简化容易给我们带来的一个 Bug, 这是我在一个实际项目中写的单元测试发现的。
下面就是我们的一个样板代码,在我们的UserRespository中有一个方法 findAll() 用于获得所有用户:1public List<User> findAll() { 2 List<User> users = new ArrayList<>(); 3 jdbcTemplate.query("select id, name from user", rs -> { 4 while (rs.next()) { 5 users.add(new User(rs.getInt("id"), rs.getString("name"))); 6 } 7 }); 8 return users; 9}
Read MoreJava 的可变参数(Varargs) 方法让我们调用起来很方便,不需要总是去构造一个数组来传递不定数量的参数,而且还可以作为方法的一个可选参数,如
void foo(int id, String...name) {
String yourName = name.length == 0 ? "Anonymous" : name[0];
......
}想要告诉名字就调用
foo(1, "Yanbin"), 不想的话就用foo(1).但我们在使用 Java Varargs 时,当变参类型定义为
Object...objects时就要当心了,因为 Object 类型的包容性原因一不小心就可以掉到坑里去了,例如下面的方法void foo(Object...objects) {
Arrays.stream(objects).forEach(System.out::println());
}当引用类型是
Object[]时调用没问题,下面代码调用可以得到预期的结果 Read More- 最近转战到 Amazon 的云服务 AWS 上,考虑到在使用它的 Lambda 服务时 Python 应用有比较可观的启动速度,与之相比而言,Java 总是慢热型,还是一个内存大户。所以有想法 Lambda 函数用 Python 来写,来增强响应速度,而内部的应用仍然采用 Java, 于是就有了 Java 与 Python 的数据交换格式。使用 Kafka 的时候是用的 Apache Avro, 因此继续考察它。
注意,本文的内容会有很大部份与前一篇 Apache Avro 序列化与反序列化 (Java 实现) 雷同,不过再经一次的了应用,了解更深了。
在不同类型语言间进行数据交换,很容易会想到用 JSON 格式,那我们为什么还要用 Apache Avro 呢?通过接下来的内容,我们可以看到以下几点:- Apache Avro 序列化的格式也是 JSON 的,Java 的 Avro 库依赖于 Jackson 库
- 序列化数据库本身带有 Schema 定义的,方便于反序列化,特别是对于 Java 代码; 而 JavaScript 会表示多此一举
- 自动支持序列化数据的压缩,在官方提供的库中,Java 可支持
deflate,snappy,bzip2, 和xz. 其他语言中可能少些,如 Python 只支持deflate, 和snappy, 应该可扩充。序列化数据中 Schema 部分不被压缩 - 天然支持序列化对象列表,这样在序列化数据中只需要一份 Schema,类似于数据库表 Schema 加上多记录行的表示方式。只用 Apache Avro 传输小对象的话,数据量比 JSON 事 JDK 序列化的数据要大。
Apache Avro 官方提供有 C, C++, C#, Java, PHP, Python 和 Ruby 的支持库,可在网上找到其他语种的类库,如 NodeJS, Go 的,等等。 Read More - 在 Java 中对于下面最简单的泛型类
class A<T> {
设想我们使用时
public void foo() {
//如何在此处获得运行时 T 的具体类型呢?
}
}new A<String>().foo();
是否能在foo()方法中获得当前的类型是 String 呢?答案是否定的,不能。在foo()方法中 this 引用给不出类型信息,this.getClass()就更不可能了,因为 Java 的泛型不等同于 C++ 的模板类,this.getClass()实例例是被所有的不同具体类型的 A 实例(new A<String>(), new A<Integer>() 等) 共享的,所以在字节码中类型会被擦除到上限。
我们可以在 IDE 的调试时看到这个泛型类的签名
Read More
在上一篇中尝试了 使用 Javassist 运行时生成泛型子类,这里要用另一个更方便的字节码增加组件 Byte Buddy 来实现类似的功能, 但代码上要直白一些。就是运用 Byte Buddy 在运行时生成一个类的子类,带泛型的,给类加上一个注解,可生成类文件或 Class 实例,不过这里更进一步,实现的方法是带参数的。
用 Byte Buddy 操作起来更简单,根本不需要接触任何字节码相关的,诸如常量池等概念。与 Javassist 相比,Byte Buddy 更为先进的是能生成的类文件都是可加载运行的,不像 Javassist 生成的类文件反编译出来是看起来是正常的,但一加载执行却不那回事。
本例所使用的 Byte Buddy 的版本是当前最新的 1.6.7,在 Maven 项目中用下面的方式引入依赖<dependency>
下面是几个需要在本例中用到的类定义 Read More
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.6.7</version>
</dependency>