测试的重要性是每个程序员都明白的, 但真正自己去做测试(Unit Test)的却很少, 曾经我也是其中的一员.
因为写个main调用一些方法, 打印出结果或状态, 然后人工肉眼去排查, 若不是迫于无奈, 我相信没有程序员愿意纠结于这些琐碎的东西.
其实, 测试本可以很有趣的.借助
JUnit, 我们可以将测试按不同的场景组织起来, 在”一键”之后的红绿条的反馈下, 快速解决代码中存在的问题. 如果你还不太了解JUnit, 请先去这里. 后文将以JUnit为基础, 以Fluent Interface(这个在国内还比较时髦的术语)为切入点, 展示一下更有趣的测试.
在解释什么是Fluent Interface之前, 请先看这样一段测试代码:
public class Calculator { public int sum(int one, int other); } public class CalculatorTest { private final Calculator calc = new Calcuator(); @Test public void 08 onePlusOne() { assertEquals(2, calc.sum(1, 1)); } } |
上述代码是基于JUnit4编写的, 用assertEquals来测试Calculator的sum方法对一加一计算的结果. 这种写法很简单, 但从语义上并不是那么流畅, 若换种写法, 如:
public class CalculatorTest { [...] @Test public void assertThatOnePlusOneIsEqualToTwo() { assertThat(calc.sum(1, 1)).isEqualTo(2); } } |
这样阅读起来是否感到更为清晰呢? 若是将语句中的符号换成空格:
assert that calc sum 1 and 1 is equal to 2
这几乎就是人类的自然语言了(囧, 尽管是e文).
也许这个例子只是让大家看到易读性的优势, 那么再看看下面这个易编写的例子:
public interface Querier { Collection<String> findNameBy(int age); } public class OrderQuerier { private final Querier querier = [...] @Test public void findNamesWithAgeInThirty() { Collection<String> names = querier.findNameBy(30); assertEquals(2, names.size()); assertTrue(names.contains("allen")); assertTrue(names.contains("john")); } @Test public void findNamesWithAgeInThirty() { Collection<String> names = querier.findNameBy(30); assertThat(names).hasSize(2).contains("allen", "john"); } } |
怎么样, 上面这个对比下, 后者是否能让你感到”清爽”呢?
assertThat风格的assert正是应用了Fluent Interface, 使得测试的代码流畅易读, 编写简单.
Fluent interface可以看作是借用了Method Chaining来实现的一种Internal DSL(Domain-Specific Language), 关于它这儿有更为全面的介绍.
前面展示的assertThat仅是FEST-Assert提供一组API的很小一部分, 它还支持其它的:
Primary Type
Object
Array
Iterator
Throwable
File
Map
除了FEST-Assert, 其实还有另一个在JUnit测试中被广泛应用的”assertThat”——hamcrest, 它使用静态导入加工厂方法实现的Internal DSL, 同样很有趣的, 不妨look一下.