引入
在 build.gradle 文件中
dependencies {
testCompile 'junit:junit:4.12'
}
这其中会引入两个jar:junit-4.12.jar 和 hamcrest-core-1.3.jar
介绍
junit 中两个重要的类 Assume 和 Assert, 以及其他一些重要的注解:BeforeClass,AfterClass,After,Before 及 Test,Ignore。
其中,BeforeClass 和 AfterClass 在每个类加载的开始和结束时运行,需要设置 static 方法;而 Before和After 则是在每个测试方法的开始之前和结束之后运行。
在 hamcrest-core 的 jar 包中,在 org.hamcrest.core 包中提供了一系列操作运算封装,使测试代码更加地易读。如is,not,allOf,anyOf等。
代码示例
@Test
public void testAssertArrayEquals() {
byte[] expected = "trial".getBytes();
byte[] actual = "trial".getBytes();
assertArrayEquals("failure - byte arrays not same", expected, actual);
}
@Test
public void testAssertEquals() {
assertEquals("failure - strings are not equal", "text", "text");
}
@Test
public void testAssertFalse() {
assertFalse("failure - should be false", false);
}
@Test
public void testAssertNotNull() {
assertNotNull("should not be null", new Object());
}
@Test
public void testAssertNotSame() {
assertNotSame("should not be same Object", new Object(), new Object());
}
@Test
public void testAssertNull() {
assertNull("should be null", null);
}
@Test
public void testAssertSame() {
Integer aNumber = Integer.valueOf(768);
assertSame("should be same", aNumber, aNumber);
}
@Test
public void testAssertTrue() {
assertTrue("failure - should be true", true);
}
以上代码来自官方介绍的 Demo , 列举的是常用而又基础的操作,但遇到复杂的集合判断操作,力不从心了,不过 Junit 提供了另一更为强大的 assertThat 方法,首先来看看它的使用:
// JUnit Matchers assertThat
@Test
public void testAssertThatBothContainsString() {
assertThat("albumen", both(containsString("a")).and(containsString("b")));
}
@Test
public void testAssertThatHasItems() {
assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));
}
@Test
public void testAssertThatEveryItemContainsString() {
assertThat(Arrays.asList(new String[] { "fun", "ban", "net" }), everyItem(containsString("n")));
}
// Core Hamcrest Matchers with assertThat
@Test
public void testAssertThatHamcrestCoreMatchers() {
assertThat("good", allOf(equalTo("good"), startsWith("good")));
assertThat("good", not(allOf(equalTo("bad"), equalTo("good"))));
assertThat("good", anyOf(equalTo("bad"), equalTo("good")));
assertThat(7, not(CombinableMatcher.<Integer>either(equalTo(3)).or(equalTo(4))));
assertThat(new Object(), not(sameInstance(new Object())));
}
这里的 assertThat 用了两种方法:一个是 JunitMatchers ,另一个是 hamcrest matchers 的 assertThat,不过后者提供的功能相当强大,前者的方法已经标为废弃了。
另外,官方也提及了其它第三方提供的 Matchers 实现:
Excel spreadsheet matchers
JSON matchers
XML/XPath matchers
所以再次我们只看后者,可以看出来的是其方法的后一个参数非常灵活,紧接着我们看看其怎么实现的?
assertThat 方法实现
public static <T> void assertThat(T actual, Matcher<? super T> matcher) {
assertThat("", actual, matcher);
}
public static <T> void assertThat(String reason, T actual,
Matcher<? super T> matcher) {
MatcherAssert.assertThat(reason, actual, matcher);
}
再定位到 MatcherAssert 类的方法 assertThat:
public static <T> void assertThat(String reason, T actual, Matcher<? super T> matcher) {
if (!matcher.matches(actual)) {
Description description = new StringDescription();
description.appendText(reason)
.appendText("
Expected: ")
.appendDescriptionOf(matcher)
.appendText("
but: ");
matcher.describeMismatch(actual, description);
throw new AssertionError(description.toString());
}
}
可以看出真正地判断方法是通过 Matcher 类的 matches 方法,若是不满足的话,则返回 AssertionError。所以真正的核心是 Matcher,而关于它的实现都在 hamcrest-core-1.3 包中,看看其实现的类结构图:
看一下其的实现,可发现上文提到的 is , anyof 等等静态方法都是返回一个相应的 Matcher,这样通过一个简单的抽象,在这里提供了极大的灵活性。若是感觉它提供的这些不满足的话,也可自己进行来进行重写,按自己的需求来定制实现。
Rule 介绍
同样地,当我们越来越多需要进行单元测试时,需要使用 Rule 来帮忙了。其主要目的是针对一个测试类中的每个单元测试方法进行统一添加一些行为。代码则使用 @Rule 注解的形式来添加至类的属性上。
在 Junit 框架中,其相对应的接口是 TestRule,而主要的实现有:
ErrorCollector: 将大量的错误收集起来
ExpectedException: 对抛出的错误做断言
ExternalResource: 可对测试方法的开始和结束添加回调
TemporaryFolder: 用来创建文件,并在测试结束时自动删除
TestName: 用来获取测试所执行的方法名称
TestWatcher: 可在测试方法的执行期间添加逻辑
Timeout: 超过固定的时间让测试结束
Verifier: 当状态不正确时,可让测试结束
它们的更多使用方法,可参照官网的 Rules 介绍。
实现原理分析
Junit4 中的测试代码可被执行,是因为其真正的入口是名为 JUnitCore。它作为 Junit 的 Facade (门面)模式,来对外进行交互。另外,其有一个静态的 main 方法:
public static void main(String... args) {
Result result = new JUnitCore().runMain(new RealSystem(), args);
System.exit(result.wasSuccessful() ? 0 : 1);
}
所以,当我们执行单元测试的时候,其实也是运行了一个新的进程应用程序,其入口在这里。我们执行分析的时候,也从这里开始: