JUnit是一个java语言的单元测试框架,是java测试里最基本的框架。
Android Studio创建的工程会自动集成JUnit,在build.gradle中添加相关依赖:
testImplementation 'junit:junit:4.12' 复制代码
测试框架的存在,并不是必须的。它们只是为了让我们写更少的测试代码。看下面示例,你会发现,所谓的单元测试方法,就像我们初学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的源码注释,如有困惑,请参考源码注释。
用于标注 public void
方法。被这个注解标记的方法,就是 一个测试方法 。一个测试类里面,可以有多个被@Test标注的测试方法。它们应该彼此独立,互不影响。可以点方法旁边的绿色箭头,单独运行一个测试方法。
也可以点击当前测试类旁边的箭头,执行测试类下面所有的测试方法。
还可以一键运行test包下面的所有测试类。
注意:有两种情况会视为测试失败,否则测试成功。
1)测试方法抛异常。
2)你可以这样使用 @Test(timeout = 100)
。设置超时时间,如果超时则视为测试失败。但这种做法是线程不安全的,所以官方推荐使用 {@link org.junit.rules.Timeout} rule
来代替。
它们是方法级别的,用于标注 public void
方法,会在被@Test标注的测试方法的一前一后运行,每调用一个测试方法都会执行一次,也就是@Before->@Test->@After。所以我们通常会在被@Before标注的方法下做数据的初始化。在被@After标注的方法下,做数据的清除。
类似@Before和@After,但不同的是它们是类级别,也就是在执行一个测试类的时候只会调用一次,而且被它们标注的方法,必须是 public static void
方法。这些方法只会运行一次,会被当前测试类里面所有的测试方法共享。当测试都需要一些昂贵的操作的时候,就需要用到这两个注解。虽然,这会让测试的独立性进行了一定的妥协,但有时这是一个有必要的优化操作。比如,数据库的连接和关闭。
这是一个非常强大的注解。
@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和@Rule相似,不同点是,@ClassRule是类级别的,只能用于标注静态成员(成员变量,成员方法),在执行一个测试类的时候只会调用一次被注解的Rule。
被该注解标记的测试方法,会被忽略,不会被运行。
测试类的执行顺序可通过对测试类添加注解 “@FixMethodOrder(value)” 。 三种执行顺序可供选择:
MethodSorters.DEFAULT:默认顺序由方法名hashcode值来决定,如果hashcode值一样,就使用MethodSorters.NAME_ASCENDING来排序。
MethodSorters.NAME_ASCENDING:按字符的字典顺序,会始终保持一致。
MethodSorters.JVM:顺序可能因运行而异,说白了就是不固定。
感觉这是一个很鸡肋注解。。
文中的相关测试例子,以及更多的测试例子均可以在 UnitTest 里面找到。
如果你对一些知识点理解不透彻,也许参考更多的博客也未必有用。我建议,你应该去好好翻阅一下官方的源码注释。这或许能帮助你更深入理解这些东西,毕竟很多博客的描述,就是源自源码注释。