1. Introduction

There are many benefits of unit testing on the Internet, but I won’t go into them here. My reason for writing unit tests is simple and crude: convenience. Imagine this scenario: We’re writing a new feature, and every time we write a part, we install it on our phone and check it out. During this process, you have to click to the corresponding page, do the corresponding action, and finally get the result back to you. If you achieve the desired results, congratulations. But if it fails this time, will the process be repeated? Are you in trouble? Time-consuming? If you want to finish writing early and leave work, you need to master unit tests. Because it greatly reduces the time you have to self-validate.

2. Preparation

When we create a new project, the template code will add JUnit’s dependency to the build file by default, and the unit test code will be placed under SRC /test/ Java, as shown below:

To execute the corresponding unit test, right-click the test method and select the “Run” option from the menu. I executed the test code in the figure and can see that the execution method took 6 milliseconds, less than 10 seconds.

3. JUnit is introduced

JUnit is Java’s most basic testing framework, and its main purpose is assertion.

Add dependencies to your app’s build file when you use it. Note: All frameworks used for test environments begin with testCompile.

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

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: Each of the above methods has a corresponding overloaded method, which can be preceded by a String argument indicating if the assertion fails.


Common comments in JUnit:

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
@FixMethodOrder Specifies the order in which methods in the test class are executed

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

4. JUnit usage

Let’s test the following simple time conversion utility class to illustrate its use.

public class DateUtil {

    /** ** ** /
    public static String FORMAT_YMDHMS = "yyyy-MM-dd HH:mm:ss";

    /** * Drop this method to enter the time to convert input such as ("2017-11-01 22:11:00") return timestamp ** @param time
     * @returnTimestamp * /
    public static long dateToStamp(String time) throws ParseException{
        SimpleDateFormat sdr = new SimpleDateFormat(FORMAT_YMDHMS, Locale.CHINA);
        Date date = sdr.parse(time);
        return date.getTime();
    }

    /** * converts the timestamp to time */
    public static String stampToDate(long lt){
        String res;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(FORMAT_YMDHMS, Locale.CHINA);
        Date date = new Date(lt);
        res = simpleDateFormat.format(date);
        returnres; }}Copy the code

1. Basic usage

1. First test the stampToDate method. In the annotation @before method, I initialized a Date object and gave a timestamp corresponding to “2017-10-15 16:00:02” time. When TESTING, I thought the result returned was equal to the string “expected time”. The following figure shows the test method after execution:

You can see that the expected values do not match the actual results, and the test failed! For the test to succeed, either the expected value is “2017-10-15 16:00:02” or assert using assertNotEquals.

2. Next, test dateToStamp method.

Very simple, I don’t think the result is equal to 4, so the test passes.

3. We noticed that in the dateToStamp method, a ParseException was thrown. This exception occurs when an argument is not passed in the specified format.

So how do we verify that a method throws an exception? You can do this by setting the Expected parameter to the @test annotation as follows:

If the corresponding exception is thrown, the test succeeds; otherwise, the test fails.

2. Parameterize tests

At this time, do you think it is still very troublesome, because every time you test a method to set the corresponding value, you can not continuously test a method with different values, save us to constantly modify. This is where @runWith and @parameters are used.

First add the @runwith (parameterized.class) annotation to the test class, and then create a public static method annotated by @Parameters to return a corresponding set of test data. Finally, the constructor is created, with the order and type of the parameters corresponding to the test data set.

A simple example is shown in the figure above, where you can see three tests in a row. The second test does not throw an exception, and the test fails!

3. AssertThat usage

For some of the basic assertions we used above, if we had not set the output for failure, only an AssertionError would have been thrown when the assertion failed, with no way of knowing which part was wrong. AssertThat solves that for us. It’s much more readable.

assertThat(T actual, Matcher<? super T> matcher);

assertThat(String reason, T actual, Matcher<? super T> matcher); 
Copy the code

Reason is the output when the assertion fails, actual is the value of the assertion, and matcher is the matcher of the assertion.

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));

The following figure shows the specific error message that is displayed when the assertThat test fails. You can see the error message is very detailed!

Of course, matchers can also be customized. Here I define a string is a mobile phone number matcher to demonstrate.

Matches and describeTo methods are implemented using the BaseMatcher abstract class. The code is as follows:

public class IsMobilePhoneMatcher extends BaseMatcher<String> {

    /** * if true is returned, the assertion succeeds; otherwise, the assertion fails

    @Override
    public boolean matches(Object item) {
        if (item == null) {
            return false;
        }

        Pattern pattern = Pattern.compile("(1|861)(3|5|7|8)\\d{9}$*");
        Matcher matcher = pattern.matcher((String) item);

        return matcher.find();
    }

    /** * adds a description */ to the object for which the assertion is expected to succeed
    @Override
    public void describeTo(Description description) {
        description.appendText("Expect this string to be a mobile phone number!");
    }

    /** * add description */ to the object whose assertion failed
    @Override
    public void describeMismatch(Object item, Description description) {
        description.appendText(item.toString() + "Not a cell phone number!"); }}Copy the code

Execute the unit tests as follows:

Correct mobile phone number tested successfully:

Error number test failed:

PS: I plan to write the content about Android unit testing. Since there are many testing frameworks involved, I will start from simple to difficult, and finally reach the stage of daily development and application. (I didn’t expect to spend a whole day on this one…) I also try to update the series as quickly as possible. I hope you can give me a lot of support!