JUnit是一个java语言的单元测试框架,是java测试里最基本的框架。
JUnit的集成
Android Studio创建的工程会自动集成JUnit,在build.gradle中添加相关依赖:
testImplementation 'junit:junit:4.12'
JUnit的示例
测试框架的存在,并不是必须的。它们只是为了让我们写更少的测试代码。看下面示例,你会发现,所谓的单元测试方法,就像我们初学java时,常写的main方法。
下面是使用JUnit进行测试的几个例子。
//测试一个有返回值的方法
@Test
public void testMethod() {
int actual = Util.add(1, 2);
assertEquals(3, actual);
}
//测试一个没有返回值,有副作用的方法
@Test
public void testMethodWithSideEffect() {
List<String> strings = Arrays.asList("a", "d", "b", "c");
List<String> excepted = Arrays.asList("a", "b", "c", "d");
//测试集合工具类的排序方法
Collections.sort(strings);
assertEquals(excepted, strings);
}
//测试一个方法抛异常的方案1
@Test
public void testMethodThrowException(){
Exception actual = null;
try {
Util.throwException();
}catch (Exception e){
actual = e;
}
assertEquals(IllegalArgumentException.class,actual.getClass());
}
//测试一个方法抛异常的方案2
@Test(expected = IllegalArgumentException.class)
public void testMethodThrowException2() {
Util.throwException();
}
如你所见,断言是单元测试里常用的东西。JUnit常用的断言方法,都在org.junit.Assert类里面,下面列出常见的几个断言方法。顾名思义,就不多说了。
assertEquals
assertNotEquals
assertArrayEquals
assertTrue
assertFalse
assertNull
assertNotNull
fail
...
JUnit的注解
相关内容主要来源于JUnit的源码注释,如有困惑,请参考源码注释。
@Test
用于标注public void
方法。被这个注解标记的方法,就是一个测试方法。一个测试类里面,可以有多个被@Test标注的测试方法。它们应该彼此独立,互不影响。可以点方法旁边的绿色箭头,单独运行一个测试方法。

也可以点击当前测试类旁边的箭头,执行测试类下面所有的测试方法。

还可以一键运行test包下面的所有测试类。

注意:有两种情况会视为测试失败,否则测试成功。
1)测试方法抛异常。
2)你可以这样使用@Test(timeout = 100)
。设置超时时间,如果超时则视为测试失败。但这种做法是线程不安全的,所以官方推荐使用{@link org.junit.rules.Timeout} rule
来代替。
@Before和@After
它们是方法级别的,用于标注public void
方法,会在被@Test标注的测试方法的一前一后运行,每调用一个测试方法都会执行一次,也就是@Before->@Test->@After。所以我们通常会在被@Before标注的方法下做数据的初始化。在被@After标注的方法下,做数据的清除。
@BeforeClass和@AfterClass
类似@Before和@After,但不同的是它们是类级别,也就是在执行一个测试类的时候只会调用一次,而且被它们标注的方法,必须是public static void
方法。这些方法只会运行一次,会被当前测试类里面所有的测试方法共享。当测试都需要一些昂贵的操作的时候,就需要用到这两个注解。虽然,这会让测试的独立性进行了一定的妥协,但有时这是一个有必要的优化操作。比如,数据库的连接和关闭。
@Rule
这是一个非常强大的注解。
@Rule是方法级别的,每个测试方法执行时都会调用被标注的Rule。
可以标注是public
但非静态,且是TestRule子类的成员变量,如:
@Rule
public TemporaryFolder folder= new TemporaryFolder();
也可以标注是public
但非静态,而且必须返回一个TestRule子类的实例的方法,如:
private TemporaryFolder folder= new TemporaryFolder();
@Rule
public TemporaryFolder getFolder() {
return folder;
}
TestRule是一个接口类,可以利用它,添加自定义的检查,或者执行必要的设置或清理。TestRule可以做@Before、@After、@BeforeClass和@AfterClass这几个注解能做的所有事,但它往往更强大。
junit也为我们提供了几个TestRule的具体实现,如Timeout、TemporaryFolder。下面给出一个使用示例:
public class Test {
//只需要用@Rule标注一个是TestRule子类的成员变量。
//这样,如果该类中的任意测试方法超过5000ms任未完成,则该测试失败。
@Rule
public Timeout timeout = new Timeout(5000);
//其余代码略...
}
我们也完全可以实现属于自己的TestRule,将自己测试类里面常用的设置或者清理等重复性高的代码,封装进去,简化测试代码。
public class YourRule implements TestRule {
@Override
public Statement apply(final Statement base, final Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
//做一些预先的设置,或者任何你想做的事
//如数据库表的创建
......
//这里就是测试方法(包括了before、after在里面)
//下面是官方源码里,对Statement接口的描述
//Represents one or more actions to be taken at runtime in the course
//of running a JUnit test suite.
base.evaluate();
//做一些清理,或者任何你想做的事
//如数据库表的删除
......
}
};
}
}
注:如果是对TestRule的具体实现仍然感到迷惑,也可以参考官方给出的那几个具体实现。
@ClassRule
@ClassRule和@Rule相似,不同点是,@ClassRule是类级别的,只能用于标注静态成员(成员变量,成员方法),在执行一个测试类的时候只会调用一次被注解的Rule。
@Ingore
被该注解标记的测试方法,会被忽略,不会被运行。
@FixMethodOrder
测试类的执行顺序可通过对测试类添加注解 “@FixMethodOrder(value)” 。 三种执行顺序可供选择:
MethodSorters.DEFAULT:默认顺序由方法名hashcode值来决定,如果hashcode值一样,就使用MethodSorters.NAME_ASCENDING来排序。
MethodSorters.NAME_ASCENDING:按字符的字典顺序,会始终保持一致。
MethodSorters.JVM:顺序可能因运行而异,说白了就是不固定。
感觉这是一个很鸡肋注解。。
后记
文中的相关测试例子,以及更多的测试例子均可以在UnitTest里面找到。
如果你对一些知识点理解不透彻,也许参考更多的博客也未必有用。我建议,你应该去好好翻阅一下官方的源码注释。这或许能帮助你更深入理解这些东西,毕竟很多博客的描述,就是源自源码注释。