Junit

Java is the most widely used and basic testing framework. Junit’s dependencies are automatically introduced when you create a project using Android Studio.

TestImplementation 'junit: junit: 4.13.2'Copy the code

However, when using my framework, I need to remove this dependency to avoid dependency conflicts.

Simple to use

Junit uses the @test annotation to determine if a method is a Test method. The name of the method can be arbitrary, but the code is readable. Use @before to prepare for tests, such as initializing objects, opening files, and so on, which will be called once Before each test method call. So if a test class has multiple test methods, the @before method will be called multiple times when the test class is run. The corresponding is @after, which is called After each test method is run and can be used to close files, etc.

Similarly, @beforeClass and @afterClass. The @beforeClass function is to execute a method decorated by @beforeClass once before running all the test methods of a test class, and the methods decorated by @AfterClass are executed after all the test methods have been executed. These two annotation methods can be used to setup and release public resources. Note that methods decorated with these two annotations must be static.

public class CalculatorTest { Calculator calculator; @BeforeClass public static void prepare(){ System.out.println("BeforeClass"); } @before public void setUp() throws Exception {system.out.println (" test preparation "); calculator = new Calculator(); } @test public void testAdd() {system.out.println (" testAdd()"); int sum = calculator.add(2, 3); Assert.assertEquals(5, sum); } @test public void testMulti() {system.out.println (" testMulti()"); int product = calculator.multi(2, 3); Assert.assertEquals(6, product); } @after public void tearDown() throws Exception {system.out.println (" end of test "); } @AfterClass public static void finish(){ System.out.println("AfterClass"); }}Copy the code

Run the test class and the result is as follows:

Commonly used method

JUnit provides a number of Assert methods to verify results. These are in the Assert class.

  • assertEquals(expected, actual)Verify that the expected value is the same as the actual value. If so, the test passes; otherwise, the test fails.
  • assertEquals(expected, actual, tolerance)The expected and actual passed in here are of type float or double, and the last argument is the deviation. If the difference between the two numbers is within this deviation, the test passes, otherwise the test fails.
  • assertTrue(boolean condition)Verify that the value of contidion is true
  • assertFalse(boolean condition)Verify that the value of contidion is false
  • assertNull(Object obj)Verify that obj is null
  • assertNotNull(Object obj)Verify that obj is not null
  • assertSame(expected, actual)Verify that expected and actual are the same object, that is, pointing to the same object
  • assertNotSame(expected, actual)Verify that expected and actual are not the same object, that is, pointing to different objects
  • fail()Failing a test method can be used to verify that your test code really does run or that a method being tested will throw an exception correctly.

Note: Each of the above methods has an overloaded method, which can be preceded by a String parameter indicating that if the validation fails, the String will be reported as the result of the failure. AssertEquals (“Current user Id should be 1”, 1, currentUser.id()); When currentUser.id() is not 1, “Current user ID should be 1” will be displayed in the result report to make the test result more readable and clear about the cause of the error.

Use of Junit Rule

A Junit Rule is a class that implements TestRule to execute some code before and after each test method. As you can see from the source code, Junit Rule has the following:

Here are just a few commonly used ones:

  1. ExpectedException: Supports specifying the type of exception expected to be thrown in a test, similar to@Test(expected = xxxException.class).
  2. ExternalResource: Supports the management of resources such as sockets and database links, which are created in advance and destroyed after the test.
  3. TemporaryFolder: Support for creating files and directories and deleting them at the end of a test run for standalone tests that use file systems.
  4. TestName: Allows you to get the name of the current test method in a test.
  5. TestWatcher: Provides five trigger points: test start, test completion, test success, test skip, and test failure, allowing us to execute custom logic at each trigger point.
  6. Timeout: Supports setting the same timeout for all test methods in a test class, similar to@Test(timeout = 100).

Use of native rules

The way to use these rules is simple: Define a public field for a class, which must be public, and use the @rule modifier. Take Timeout as an example:

public class JunitRuleTest { @Rule public Timeout timeout = new Timeout(10, TimeUnit.MILLISECONDS); @Test public void testRule1() throws InterruptedException { Thread.sleep(20); assertEquals(4, 2 + 2); } @Test public void testRule2() throws InterruptedException { Thread.sleep(5); assertEquals(4, 2 + 2); }}Copy the code

Junit Rule applies to every test method, so each test method of a test class cannot exceed the specified time or it will be marked as a test failure. So the result of running the above code is: testRule1 test fails, testRule2 test passes.

Use of custom rules

In addition, you can customize your own Rule by implementing the TestRule interface and implementing the Apply method.

class CustomRule implements TestRule { @Override public Statement apply(Statement base, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { String className  = description.getClassName(); String methodName = description.getMethodName(); System.out.println(" execute method start, className:" + className + ", methodName:" + methodName); Evaluate (); base.evaluate(); System.out.println(" execute method end, className:" + className + ", methodName:" + methodName); Base.evaluate (); base.evaluate(); }}Copy the code

Use the same method as the built-in Rule:

public class JunitRuleTest { @Rule public CustomRule customRule = new CustomRule(); @Test public void testRule2() throws InterruptedException { assertEquals(4, 2 + 2); }}Copy the code

Run it:

Difference from @before annotations

The @before annotation only applies to the current test class and its subclasses, whereas a class that implements TestRule can be used for multiple test classes, so JUnit Rule is a better choice when you want to apply the same code to multiple test classes. JUnit Rule implements all the functions of @before, @beforeClass, @After, and @AfterClass. In addition, when multiple different rule objects are used for the same test case, you can use RuleChain to set the order in which those rules are executed.

Other features

Ignore test method

In addition, when for some reason (formal code has not been implemented, etc.), we might want JUnit to ignore certain methods and not run this test method when it runs all the test methods. It’s easy to do this by prefacing the test method to be ignored with @ignore.

public class CalculatorTest {
   Calculator mCalculator;
​
   @Before
   public void setup() {
       mCalculator = new Calculator();
  }
​
   // Omit testAdd() and testMultiply() for brevity
​
   @Test
   @Ignore("not implemented yet")
   public void testFactorial() {
  }
}
Copy the code

The validation method throws some exceptions

When writing code, exceptions are often thrown, and we can specify the circumstances in which an exception should be thrown, such methods can also be tested. For example, the division function throws ArithmeticException when the divisor is 0, but we can specify to throw IllegalArgumentException as follows:

public class Calculator { public int divide(int num1, int num2){ if (num2 == 0){ throw new IllegalArgumentException("Divider cannot be 0"); } return num1/num2; }}Copy the code

In Junit, you can do this by passing an expected parameter to the @test annotation, as follows:

public class CalculatorTest { Calculator calculator; @before public void setUp() throws Exception {system.out.println (" test preparation "); calculator = new Calculator(); } @ Test (expected = IllegalArgumentException. Class) public void testDivide () {System. Out. Println (" testDivide Test method () "); calculator.divide(2, 0); }}Copy the code

@ Test (expected = IllegalArgumentException. Class) to verify the Test method will throw IllegalArgumentException exception, if didn’t sell them, then the tests fail.

Limit test method execution time

Many times we need to limit the execution time of certain test methods so that we can control the testing time. In Junit, you can do this by passing a timeout argument to the @test annotation, as follows:

public class ExampleUnitTest { @Test(timeout = 5) public void addition_isCorrect() throws InterruptedException { Thread.sleep(10); assertEquals(4, 2 + 2); }}Copy the code

The running results are as follows:

Note, however, that the test method using the timeout annotation argument runs on a different thread than the @before and @After methods! This is what I found by looking at the source code. After testing, timeout does not work when it is used together with @before and @after. So it’s best not to use both.

​
   /**
    * Optionally specify <code>timeout</code> in milliseconds to cause a test method to fail if it
    * takes longer than that number of milliseconds.
    * <p>
    * <b>THREAD SAFETY WARNING:</b> Test methods with a timeout parameter are run in a thread other than the
    * thread which runs the fixture's @Before and @After methods. This may yield different behavior for
    * code that is not thread safe when compared to the same test method without a timeout parameter.
    * <b>Consider using the {@link org.junit.rules.Timeout} rule instead</b>, which ensures a test method is run on the
    * same thread as the fixture's @Before and @After methods.
    * </p>
    */
   long timeout() default 0L;
Copy the code

At this point, the basic applications of JUnit are covered.