转载

浅谈测试之JUnit

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标注的测试方法。它们应该彼此独立,互不影响。可以点方法旁边的绿色箭头,单独运行一个测试方法。

浅谈测试之JUnit

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

浅谈测试之JUnit

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

浅谈测试之JUnit

注意:有两种情况会视为测试失败,否则测试成功。

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 里面找到。

如果你对一些知识点理解不透彻,也许参考更多的博客也未必有用。我建议,你应该去好好翻阅一下官方的源码注释。这或许能帮助你更深入理解这些东西,毕竟很多博客的描述,就是源自源码注释。

原文  https://juejin.im/post/5a5819baf265da3e36412fd0
正文到此结束
Loading...