In this chapter, let us understand the basics of JUnit, the popular unit-testing framework developed by the Java community upon which the espresso testing framework is build.
JUnit is the de facto standard for unit testing a Java application. Even though, it is popular for unit testing, it has complete support and provision for instrumentation testing as well. Espresso testing library extends the necessary JUnit classes to support the Android based instrumentation testing.
Let us create a Java class, Computation (Computation.java) and write simple mathematical operation, Summation and Multiplication. Then, we will write test cases using JUnit and check it by running the test cases.
Start Android Studio.
Open HelloWorldApp created in the previous chapter.
Create a file, Computation.java in app/src/main/java/com/howcodex/espressosamples/helloworldapp/ and write two functions – Sum and Multiply as specified below,
package com.howcodex.espressosamples.helloworldapp; public class Computation { public Computation() {} public int Sum(int a, int b) { return a + b; } public int Multiply(int a, int b) { return a * b; } }
Create a file, ComputationUnitTest.java in app/src/test/java/com/howcodex/espressosamples/helloworldapp and write unit test cases to test Sum and Multiply functionality as specified below
package com.howcodex.espressosamples.helloworldapp; import org.junit.Test; import static org.junit.Assert.assertEquals; public class ComputationUnitTest { @Test public void sum_isCorrect() { Computation computation = new Computation(); assertEquals(4, computation.Sum(2,2)); } @Test public void multiply_isCorrect() { Computation computation = new Computation(); assertEquals(4, computation.Multiply(2,2)); } }
Here, we have used two new terms – @Test and assertEquals. In general, JUnit uses Java annotation to identify the test cases in a class and information on how to execute the test cases. @Test is one such Java annotation, which specifies that the particular function is a junit test case. assertEquals is a function to assert that the first argument (expected value) and the second argument (computed value) are equal and same. JUnit provides a number of assertion methods for different test scenarios.
Now, run the ComputationUnitTest in the Android studio by right-clicking the class and invoking the Run 'ComputationUnitTest' option as explained in the previous chapter. This will run the unit test cases and report success.
Result of computation unit test is as shown below −
The JUnit framework uses annotation extensively. Some of the important annotations are as follows −
@Test
@Before
@After
@BeforeClass
@AfterClass
@Rule
@Test is the very important annotation in the JUnit framework. @Test is used to differentiate a normal method from the test case method. Once a method is decorated with @Test annotation, then that particular method is considered as a Test case and will be run by JUnit Runner. JUnit Runner is a special class, which is used to find and run the JUnit test cases available inside the java classes. For now, we are using Android Studio’s build in option to run the unit tests (which in turn run the JUnit Runner). A sample code is as follows,
package com.howcodex.espressosamples.helloworldapp; import org.junit.Test; import static org.junit.Assert.assertEquals; public class ComputationUnitTest { @Test public void multiply_isCorrect() { Computation computation = new Computation(); assertEquals(4, computation.Multiply(2,2)); } }
@Before annotation is used to refer a method, which needs to be invoked before running any test method available in a particular test class. For example in our sample, the Computation object can be created in a separate method and annotated with @Before so that it will run before both sum_isCorrect and multiply_isCorrect test case. The complete code is as follows,
package com.howcodex.espressosamples.helloworldapp; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertEquals; public class ComputationUnitTest { Computation computation = null; @Before public void CreateComputationObject() { this.computation = new Computation(); } @Test public void sum_isCorrect() { assertEquals(4, this.computation.Sum(2,2)); } @Test public void multiply_isCorrect() { assertEquals(4, this.computation.Multiply(2,2)); } }
@After is similar to @Before, but the method annotated with @After will be called or executed after each test case is run. The sample code is as follows,
package com.howcodex.espressosamples.helloworldapp; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertEquals; public class ComputationUnitTest { Computation computation = null; @Before public void CreateComputationObject() { this.computation = new Computation(); } @After public void DestroyComputationObject() { this.computation = null; } @Test public void sum_isCorrect() { assertEquals(4, this.computation.Sum(2,2)); } @Test public void multiply_isCorrect() { assertEquals(4, this.computation.Multiply(2,2)); } }
@BeforeClass is similar to @Before, but the method annotated with @BeforeClass will be called or executed only once before running all test cases in a particular class. It is useful to create resource intensive object like database connection object. This will reduce the time to execute a collection of test cases. This method needs to be static in order to work properly. In our sample, we can create the computation object once before running all test cases as specified below,
package com.howcodex.espressosamples.helloworldapp; import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.assertEquals; public class ComputationUnitTest { private static Computation computation = null; @BeforeClass public static void CreateComputationObject() { computation = new Computation(); } @Test public void sum_isCorrect() { assertEquals(4, computation.Sum(2,2)); } @Test public void multiply_isCorrect() { assertEquals(4, computation.Multiply(2,2)); } }
@AfterClass is similar to @BeforeClass, but the method annotated with @AfterClass will be called or executed only once after all test cases in a particular class are run. This method also needs to be static to work properly. The sample code is as follows −
package com.howcodex.espressosamples.helloworldapp; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.assertEquals; public class ComputationUnitTest { private static Computation computation = null; @BeforeClass public static void CreateComputationObject() { computation = new Computation(); } @AfterClass public static void DestroyComputationObject() { computation = null; } @Test public void sum_isCorrect() { assertEquals(4, computation.Sum(2,2)); } @Test public void multiply_isCorrect() { assertEquals(4, computation.Multiply(2,2)); } }
@Rule annotation is one of the highlights of JUnit. It is used to add behavior to the test cases. We can only annotate the fields of type TestRule. It actually provides feature set provided by @Before and @After annotation but in an efficient and reusable way. For example, we may need a temporary folder to store some data during a test case. Normally, we need to create a temporary folder before running the test case (using either @Before or @BeforeClass annotation) and destroy it after the test case is run (using either @After or @AfterClass annotation). Instead, we can use TemporaryFolder (of type TestRule) class provided by JUnit framework to create a temporary folder for all our test cases and the temporary folder will be deleted as and when the test case is run. We need to create a new variable of type TemporaryFolder and need to annotate with @Rule as specified below,
package com.howcodex.espressosamples.helloworldapp; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import java.io.File; import java.io.IOException; import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; public class ComputationUnitTest { private static Computation computation = null; @Rule public TemporaryFolder folder = new TemporaryFolder(); @Test public void file_isCreated() throws IOException { folder.newFolder("MyTestFolder"); File testFile = folder.newFile("MyTestFile.txt"); assertTrue(testFile.exists()); } @BeforeClass public static void CreateComputationObject() { computation = new Computation(); } @AfterClass public static void DestroyComputationObject() { computation = null; } @Test public void sum_isCorrect() { assertEquals(4, computation.Sum(2,2)); } @Test public void multiply_isCorrect() { assertEquals(4, computation.Multiply(2,2)); } }
In JUnit, the methods annotated with different annotation will be executed in specific order as shown below,
@BeforeClass
@Rule
@Before
@Test
@After
@AfterClass
Assertion is a way of checking whether the expected value of the test case matches the actual value of the test case result. JUnit provides assertion for different scenario; a few important assertions are listed below −
fail() − To explicitly make a test case fail.
assertTrue(boolean test_condition) − Checks that the test_condition is true
assertFalse(boolean test_condition) − Checks that the test_condition is false
assertEquals(expected, actual) − Checks that both values are equal
assertNull(object) − Checks that the object is null
assertNotNull(object) − Checks that the object is not null
assertSame(expected, actual) − Checks that both refers same object.
assertNotSame(expected, actual) − Checks that both refers different object.