In my previous Android development career, barely used unit test, never seen anyone to introduce, as if this is not very important thing in the domestic developers eyes, there is no specific time or most of the development of students to use the unit test framework, perhaps more important reason is my personal informed.

background

What are unit tests?

Unit testing is writing test code for the smallest unit. In Java, the smallest unit of functionality is a method, so unit testing a Java program is testing a single Java method.

Why do unit tests

In foreign countries, the actual development process is often, write the test first, after the test is written, then start to write the implementation code. In the concrete implementation process, while writing while testing, when all the tests passed, it represents the completion of the development task. This is also known as TDD(Test Driven Development).


Introduction to the

Junit is an open source unit testing framework for the Java language, designed specifically for Java and most widely used. (Of course, Kotlin is also ok to use, just pay attention to some small details)

Depend on the way

Maven

<dependencies>
 	<dependency>
    	<groupId>junit</groupId>
    	<artifactId>junit</artifactId>
    	<version>4.12</version>
    	<scope>test</scope>
	</dependency>
</dependencies>
Copy the code

Gradle

dependencies {
	testImplementation 'junit: junit: 4.12'
}
Copy the code

The main method

The main methods in the Assert class are as follows:

The method name Methods described
assertEquals Assert that the expected value passed in is equal to the actual value
assertNotEquals Assert that the expected value passed in is not equal to the actual value
assertArrayEquals Asserts that the expected array passed in is equal to the actual array
assertNull Asserts that the passed object is null
assertNotNull Asserts that the passed object is not null
assertTrue Assert the condition is true
assertFalse The assertion condition is false
assertSame Assert that two objects refer to the same object, equivalent to “==”
assertNotSame Asserting that two objects refer to different objects is equivalent to “! =”
assertThat Asserts whether the actual value satisfies the specified condition

Note that all of the above methods have their own overloaded methods, which can be preceded by a String parameter indicating that the assertion fails.


Commonly used annotations

Execution order: @beforeClass – > @before – > @test – > @after – > @AfterClass

Annotation name meaning
@Test Indicates that this method is a test method
@Before Executed in front of each test method for initialization
@After Execute after each test method to free resources
@Ignore Ignored test methods
@BeforeClass Runs in front of all methods in the class. The method that this annotation decorates must be static void
@AfterClass Runs last in the class. The method that this annotation decorates must be static void
@RunWith Specifies that the test class uses a runner
@Parameters Specifies the test data collection for the test class
@Rule Reformulate the behavior of the methods in the test class
@FixMethodOrder Specifies the order in which methods in the test class are executed

use

Based on using

So let’s say we have an algorithm that’s equivalent to parentheses.

StackExample.kt

Equivalent braces / * * *, such as: given a string represented by the sequence of brackets, contain the following characters: '(',') ', '{','} ', '[' and'] ', to determine whether effective brackets sequence. * parentheses must be in accordance with the "()" order said, "() [] {}" is a valid brackets, but "([]" bracket is invalid. * * Solution idea: * Use stack storage to cut a string into char traversal, first storing symbols in the specified direction, such as '(','{','['). * return false * */ if the stack top symbol is equal to the current char and the stack is not null
fun isBrackets(str: String): Boolean {
    if (str.length < 2) return false
    val stack = Stack<Char>()
    str.forEach {
        if ("{([".contains(it)) {
            stack.push(it)
        } else {
            if (stack.isNotEmpty() && isBracketChart(stack.peek(), it)) {
                stack.pop()
            } else return false}}return stack.isEmpty()
}


private fun isBracketChart(str1: Char, str2: Char): Boolean =
    (str1 == '{' && str2 == '} ') ||
            (str1 == '[' && str2 == '] ') || (str1 == '(' && str2 == ') ')
Copy the code

Code testing (not using Junit)

If we were not using Junit, we might have written the following test code:

fun main(a) {
    println(isBrackets("{}"))
  	xxxx...
}
Copy the code

In contrast, if we added other methods, the main() method would have to be changed frequently, and the correctness of the test would not be intuitive.

Using Junit

We create a class like StackExampleKtTest under the corresponding test package, or we directly use the following shortcut, using MAC (Option + Enter) and Windows (CTRL + Enter) before the corresponding method, as shown in the figure

The following is an example:

StackExampleKtTest

class StackExampleKtTest {

    @Test
    fun testIsBrackets(a) {
        assertArrayEquals(
            booleanArrayOf(
                isBrackets(""),
                isBrackets("{"),
                isBrackets("{{}}"),
                isBrackets("{} ({})"),
                isBrackets("{{}"),
            ), booleanArrayOf(
                false.false.true.true.false))}}Copy the code

Parametric test

The above method of use, if we have to set the corresponding value every time we test a method, it is relatively tedious, then how to use successive different values to test the same method, so that we can avoid not to modify for many times, save some time. Use @runWith and @parameters.

Add a RunWith(paramterize.class) annotation to the test class, create a static method annotated by @paramters that returns a set of test data, and create a constructor whose parameters are in a sequence corresponding to the set of test data.

The following is an example:

@RunWith(Parameterized::class)
class StackExampleKtTest(private val str: Pair<String, Boolean>) {

    // TODO:In turn, this approach makes it hard to find errors
    companion object {
        @Parameterized.Parameters
        @JvmStatic
        fun primeStrings(a): Collection<Pair<String, Boolean>> =
            listOf(
                "" to false."{" to false."{}" to false."{[]}" to true."asd{()}" to false)}@Test
    fun testIsBrackets(a) {
      	// Notice the error message here
        assertEquals("Error - current \"${str.first}\" to ${str.second}",
            str.second, isBrackets(str.first))
    }
}
Copy the code

Note: The corresponding @parameterized. Parameters method used in Kotlin requires the addition of @jVMStatic. In the process of using this parameterized test, if we do not add an error message, it may not be easy to find which test case is wrong when looking for problems, so we need to pay attention to this.

AssertThat usage

Used to improve readability of output after assertion failures. By default, assertion failures only raise an AssertionError, and we have no way of knowing what went wrong, and assertThat is there to solve that problem.

Common matcher arrangement:

matcher instructions example
is The assertion argument is equal to the matching expression given later assertThat(5, is (5));
not The assertion argument is not equal to the matching expression given later assertThat(5, not(6));
equalTo Assert argument equality assertThat(30, equalTo(30));
equalToIgnoringCase Assert that strings are equal ignoring case AssertThat (” Ab “, the equalToIgnoringCase (” Ab “));
containsString The assertion string contains a certain string AssertThat (” ABC “, containsString (” BC “));
startsWith The assertion string starts with a string AssertThat (” ABC “, startsWith (” a “));
endsWith The assertion string ends with a string AssertThat (” ABC “, endsWith (” c “));
nullValue The value of the assertion parameter is NULL assertThat(null, nullValue());
notNullValue Asserts that the value of the argument is not null AssertThat (” ABC “, notNullValue ());
greaterThan Assert parameter greater than assertThat(4, greaterThan(3));
lessThan The assertion parameter is less than assertThat(4, lessThan(6));
greaterThanOrEqualTo Assert that arguments are greater than or equal to assertThat(4, greaterThanOrEqualTo(3));
lessThanOrEqualTo Assert arguments are less than or equal to assertThat(4, lessThanOrEqualTo(6));
closeTo Asserts that floating point numbers are in a certain range AssertThat (4.0, closeTo (2.6, 4.3));
allOf An assertion meets all criteria and is equivalent to && assertThat(4,allOf(greaterThan(3), lessThan(6)));
anyOf To assert that a condition is met, equivalent to or assertThat(4,anyOf(greaterThan(9), lessThan(6)));
hasKey Assert that the Map collection contains this key AssertThat (map, hasKey (” key “));
hasValue Assert that the Map collection contains this value assertThat(map, hasValue(value));
hasItem The assertion iterator contains this element assertThat(list, hasItem(element));

@Rule

During the test process, we can also add @before or @after to provide a prompt Before and After the test, but it may be a bit cumbersome to write this every time. So you can use @rule at this point.

The following is an example:

TestPrompt

 class TestPormpt : TestRule {
    override fun apply(statement: Statement, description: Description): Statement {

        // Get the test method name
        val methodName: String = description.methodName
        // equivalent to @before
        println(methodName + "Before the test begins!")

        // The test method to run
        statement.evaluate()

        // Run complete, equivalent to @after
        println(methodName + "Test over!")
        return statement
    }
}
Copy the code
class StackExampleKtTest {

    // TODO: 2020/11/15
    // Note: When used in Kotlin, you need to change @rule to @get:Rule
    //  或者 使用 @Rule @JvmField
    //@get:Rule
    @Rule @JvmField
    public val prompt = TestPormpt()
  
// @Before
// fun testStart(){
// println(" test open ")
/ /}
//
// @After
// fun testStop(){
//        println("测试关")
/ /}

    @Test
    fun testThat(a) {
        assertThat("123", equalTo("123"))}}Copy the code

reference

  • Liao Xuefeng – Writing JUnit tests
  • Android Unit Testing part 1: Use of the JUnit framework