Detailed explanation of the use of JUnit unit testing

The JUnit testing framework discussed in this article is based on JUnit4.x. And will explain the use of JUnit through Android Studio.
In many places, it cannot be completely described in Chinese, so some places need to be described in English.
This article focuses on conceptual content and lays the groundwork for subsequent test cases

Define test methods

JUnit uses annotations to annotate test methods and configure them, very important annotations for JUnit4 are given in the table below. All annotations below apply to methods .

JUnit 4 describe
import org.junit.* Used to import the following annotations.
@Test Mark the method as a test method.
@Before Executed before each test, generally used to prepare the test environment (initializing classes, etc.).
@After Executed after each test to clean up the test environment (e.g. delete temporary data, restore defaults, etc.). It can also save memory by cleaning up expensive memory structures.
@BeforeClass Execute once before all tests. It is generally used to perform time intensive activities, such as connecting to a database, etc. Methods marked with this annotation need to be defined as static void.
@AfterClass Executed once after all test executions are complete. It is generally used to clean up some activities, such as disconnecting data. Methods marked with this annotation need to be defined as static void
@Ignoreor@Ignore("Why disabled") Test methods marked with this annotation are disabled. This is very useful when the actual code is modified but the test code is not, or if the test is not included in the test because it takes too long to execute, it is best to provide a reason for not testing it.
@Test (expected = Exception.class) If this test method does not throw the assigned exception (Exception.class) it will fail.
@Test(timeout=100) If this test method executes for more than 100 milliseconds it will fail.

Assert statements

JUnit provides static methods to test certain conditions through the Assert class. These assertion statements usually begin with assert. They allow you to specify error messages, expected results, and actual results. Assertion methods compare the actual value returned by the test with the expected value. An AssertionException is thrown if the comparison fails.

The following table briefly describes these methods. The parameter in [] is an optional string type.

statement describe
fail([message]) Fail this test method, possibly to check if a part of the code is not being executed, or if there is a failing test before the test code is executed. The message parameter is optional.
assertTrue([message,] boolean condition) Verify that the boolean condition is true.
assertFalse([message,] boolean condition) Verify that the boolean condition is false.
assertEquals([message,] expected, actual) Verify that expected and actual are the same. Note: For arrays, the reference is checked instead of the contents of the array .
assertEquals([message,] expected, actual, tolerance) Verify that float or double matches. We all know that computers represent floating-point data with a certain deviation, so even if they are theoretically equal, they may not be represented by a computer, so a deviation value is passed in here. If the difference between the two numbers is within this deviation value, the test passes, otherwise the test fails.
assertNull([message,] object) Verify that object is null.
assertNotNull([message,] object) Verify that object is not null.
assertSame([message,] expected, actual) Verify that expected and actual are the same object.
assertNotSame([message,] expected, actual) Verify that expected and actual are not the same object.

JUnit test suites

If you have several test classes then you can use combined tests. This way a composite test at runtime will execute all the test classes in the composite test in order. In addition, combined tests can also contain other combined tests. (Ugh... a bit roundabout). Simply put, it can execute multiple test cases at once.

The following example demonstrates the use of combined tests. It contains two test classes MyClassTest and CalculatorTest. If you continue to want to add test classes, you can continue to put them in the @SuiteClasses annotation.

package com.lulu.androidtestdemo;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
/**
 * Created by zhanglulu on 2018/1/24.
 */
@RunWith(Suite.class)
@Suite.SuiteClasses({
 MyClassTest.class,CalculatorTest.class
})
public class AllTest {
}

Disable testing

在上文中提到可以使用@ignore注解来标记禁用当前测试方法。或者也可以使用Assume.assumeFalse或Assume.assumeTrue来判断测试条件。Assume.assumeFalse方法的测试条件返回true则表示当前测试方法无效,同理Assume.assumeTrue的测试条件返回false表示当前测试方法无效。

下图中示例就是在Windows系统中禁用addition_isCorrect()方法。其中System.getProperty(“os.name”).contains(“Windows”)称之为测试条件

package com.lulu.androidtestdemo;

import org.junit.Assume;
import org.junit.Test;

import static org.junit.Assert.assertEquals;

/**
 *禁用测试
 */
public class ExampleUnitTest {
    @Test
    public void addition_isCorrect() throws Exception {
        Assume.assumeFalse(System.getProperty("os.name").contains("Windows"));
        assertEquals(4, 2 + 2);
    }
}

参数化测试(Parameterized test)

JUnit可以在测试类使用参数进行测试。这个类包含一个待测试的方法,并且这个方法可以用不同的参数去执行。

要想将测试类标记为参数化测试,必须使用注解:@RunWith(Parameterized.class)

这样的测试类必须包含一个静态方法,这个方法要使用@Parameters注解标注。而且这个方法要生成并返回一个数组的集合(对,没错,是数组的集合Collection

/**
 * Created by zhanglulu on 2018/1/24.
 * 参数化测试示例
 */
@RunWith(Parameterized.class)
public class ParameterizedTestFields {
    @Parameterized.Parameter(0)
    public int m1;
    @Parameterized.Parameter(1)
    public int m2;
    @Parameterized.Parameter(2)
    public int result;

    // creates the test data
    @Parameterized.Parameters
    public static Collection<Object[]> data() {
        Object[][] data = new Object[][] { { 1 , 2, 2 }, { 5, 3, 15 }, { 121, 4, 484 } };
        return Arrays.asList(data);
    }
    @Test
    public void testMultiplyException() {
        MyClass tester = new MyClass();
        assertEquals("Result", result, tester.multiply(m1, m2));
    }
}

Note: 必须提供@Parameter方法,方法的返回必须是public static Collection,不能有参数,并且collection元素必须是相同长度的数组。同时数组的长度必须与测试类的字段(m1,m2,result)的数量相匹配。

如果不使用 @Parameter()标注公共字段,也可以使用构造方法给字段赋值,如代码所示:

/**
 * Created by zhanglulu on 2018/1/24.
 * 参数化测试示例
 */
@RunWith(Parameterized.class)
public class ParameterizedTestFields {
    public int m1;
    public int m2;
    public int result;

    public ParameterizedTestFields(int m1, int m2, int result) {
        this.m1 = m1;
        this.m2 = m2;
        this.result = result;
    }
    // creates the test data
    @Parameterized.Parameters
    public static Collection<Object[]> data() {
        Object[][] data = new Object[][] { { 1 , 2, 2 }, { 5, 3, 15 }, { 121, 4, 484 } };
        return Arrays.asList(data);
    }
    @Test
    public void testMultiplyException() {
        MyClass tester = new MyClass();
        assertEquals("Result", result, tester.multiply(m1, m2));
    }
}

Note : 需要注意的是两者必须有其一,否则会抛出反射参数不匹配的异常:java.lang.IllegalArgumentException: wrong number of arguments

还有其他的实现方案,这里不再详述请移步:https://github.com/Pragmatists/JUnitParams.

分类测试(Category)

Category继承自Suit,可以让我们的测试类中的测试方法进行分类测试,下面的例子就介绍了如何使用Category快速的进行分类测试。

public class A {
    @Test
    public void a() {
        fail();
    }
    @Category(SlowTests.class)
    @Test
    public void b() {
    }
}
@Category({ SlowTests.class, FastTests.class })
public class B {
    @Test
    public void c() {
    }
}
public interface FastTests {

}
public interface SlowTests {

}

进行分类测试:

@RunWith(Categories.class)
@Categories.IncludeCategory(SlowTests.class)
@Suite.SuiteClasses({ A.class, B.class })
public class SlowTestSuite {

}

JUnit 规则 (JUnit Rule)

简介

Rule是JUnit4的新特性,它可以让我们扩展JUnit的功能,灵活地改变测试方法的行为。JUnit中使用@Rule和@ClassRule两个注解来实现Rule的扩展,这两个注解需要放在实现了TestRule接口的成员变量(@Rule)或者静态变量(@ClassRule)上。@Rule和@ClassRule的不同点是,@Rule是方法级别的,每个测试方法执行时都会调用被注解的Rule,而@ClassRule是类级别的,在执行一个测试类的时候只会调用一次被注解的Rule。

JUnit内置的Rule

ExpectedException

例如下面的示例,用来检测抛出的异常信息是否符合预期。当methodToBeTest方法中抛出IllegalArgumentException异常,并且异常信息中含有“Negative value not allowed”信息时,测试通过。

package com.lulu.androidtestdemo.junit.rule;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

/**
 * Created by zhanglulu on 2018/1/24.
 * JUnit Rules
 */
public class RuleExceptionTesterExample {
    @Rule
    public ExpectedException exception = ExpectedException.none();

    @Test
    public void throwsIllegalArgumentExceptionIfIconIsNull() {
        exception.expect(IllegalArgumentException.class);
        exception.expectMessage("Negative value not allowed");
        ClassToBeTested t = new ClassToBeTested();
        t.methodToBeTest(-1);
    }
}

class ClassToBeTested {

    public void methodToBeTest(int i) {
        if (i == -1) {
            throw new IllegalArgumentException("Negative value not allowed");
        }
    }
}

TemporaryFolder

TemporaryFolder 类可以在测试之前创建临时的文件或文件夹,并且在测试结束后自动删除。

package com.lulu.androidtestdemo.junit.rule;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.File;
import java.io.IOException;

import static org.junit.Assert.assertTrue;

/**
 * Created by zhanglulu on 2018/1/24.
 * testUsingTempFolder
 */
public class RuleTester {
    @Rule
    public TemporaryFolder folder = new TemporaryFolder();

    @Test
    public void testUsingTempFolder() throws IOException {
        File createdFolder = folder.newFolder("newfolder");
        File createdFile = folder.newFile("myfilefile.txt");
        assertTrue(createdFile.exists());
        assertTrue(createdFolder.exists());
    }
}

更多的已经实现好的Rule请移步:https://github.com/junit-team/junit4/wiki/Rules

敲行代码再睡觉

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324406575&siteId=291194637
Recommended