Unit Testing with JUnit
JUnit is a unit testing framework for the Java programming language. JUnit has been important in the development of test-driven development (TDD), and is one of a family of unit testing frameworks collectively known as xUnit, that originated with JUnit.
It allows you to write simple test cases with a simple @Test annotation, like:
@Test
public void newArrayListsHaveNoElements() {
assertThat(new ArrayList().size(), is(0));
}
Though it was a bit early to show a test case, i wanted to demonstrate how easy it is to get started.
JUnit is Java's version of unit testing. The idea of unit testing is to test individual components of the project, rather than the workflow, ensuring that all pieces are working individually. Then, if the design itself is correct, the project will ideally be correct as well. It also helps provide stability in future updates.
Let’s quickly have a look at the features JUnit offers towards a project.
Features of JUnit
- JUnit is an open source framework, which is used for writing and running automated tests.
- Provides simple annotations to identify test methods.
- Provides assertions for testing expected results.
- Provides test runners for running tests.
- JUnit tests allow you to write codes faster, which helps to create bug-free code.
- JUnit is simple. It is less complex and takes less time to understand and to be integrated into the code.
- JUnit tests can be run automatically and they check their own results and provide immediate feedback. There's no need to manually comb through a report of test results.
- JUnit tests can be organized into test suites containing test cases and even other test suites.
- JUnit shows test progress in a bar that is green if the test is running smoothly, and it turns red when a test fails.
JUnit vs Manual Testing
When going through the actual need of JUnit testing and investing time to write unit-test cases, some questions arise.
- Do you really want to hand-check 1000 print statements? Wouldn't you rather have the computer do it for you and just tell you "yep, everything's all right" so you can get back to coding?
- They can be run by a script as part of a build, or on every commit. Used in this way, nobody has to remember to run them (and being human, we'll often forget).
- The test framework has already been written and debugged. If you're worried about introducing bugs in the course of building your tests, I'd worry far more about making a mistake in the construction of an entire testing framework from scratch than about making a simple logical error in a few tests.
- As second points out, xUnit-type frameworks are so common that they're usually integrated into other types of tools—IDEs will run them, and continuous integration tools like Hudson can parse their logs and track their success/failure rate over time, or notify you via email when they fail. This is all functionality you'd have to build yourself—and although you may not need/want it now, if you ever do in the future, it'll be available.
Basic Annotations Example
Some basic JUnit annotations that are easy to understand are:
- @BeforeClass – Run once before any of the test methods in the class.
- @AfterClass – Run once after all the tests in the class have been run.
- @Before – Run before @Test.
- @After – Run after @Test.
- @Test – This is the test method to run.
package com.discoversdk;
import org.junit.*;
public class DiscoverAnnotationTest {
// Run once, e.g. Make Database connection, connection pool
@BeforeClass
public static void runOnceBeforeClass() {
System.out.println("@BeforeClass - runOnceBeforeClass");
}
// Run once, e.g close connection, cleanup
@AfterClass
public static void runOnceAfterClass() {
System.out.println("@AfterClass - runOnceAfterClass");
}
// Should rename to @BeforeTestMethod
// e.g. Creating a similar object and share for all @Test
@Before
public void runBeforeTestMethod() {
System.out.println("@Before - runBeforeTestMethod");
}
// Should rename to @AfterTestMethod
@After
public void runAfterTestMethod() {
System.out.println("@After - runAfterTestMethod");
}
@Test
public void test_method_1() {
System.out.println("@Test - test_method_1");
}
@Test
public void test_method_2() {
System.out.println("@Test - test_method_2");
}
}
Output of above Test class will be:
@BeforeClass - runOnceBeforeClass
@Before - runBeforeTestMethod
@Test - test_method_1
@After - runAfterTestMethod
@Before - runBeforeTestMethod
@Test - test_method_2
@After - runAfterTestMethod
@AfterClass - runOnceAfterClass
Expected Exceptions Test
The following test class demonstrates how an expected exception test can be written:
package com.discoversdk;
import org.junit.Test;
import java.util.ArrayList;
public class DiscoverExceptionTest {
@Test(expected = ArithmeticException.class)
public void testDivisionWithArithmeticException() {
int i = 1 / 0;
}
@Test(expected = IndexOutOfBoundsException.class)
public void testEmptyList() {
new ArrayList<>().get(0);
}
}
Ignore test cases
Ignoring a test case is a simple necessity in some projects at some point. This might happen when the actual test for a code is not yet completely ready.
Here is a simple way to ignore a test case completely, without the need of commenting it.
package com.discoversdk;
import org.junit.Ignore;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
public class DiscoverIgnoreTest {
@Test
public void testMath1() {
assertThat(1 + 1, is(2));
}
@Ignore
@Test
public void testMath2() {
assertThat(1 + 2, is(5));
}
}
The test runner will not report the test. In IDE, the test runner will display the ignored tests with a different icon or color and highlight them, so that you know what tests are ignored.
For large projects, many developers are handling different modules. A failed test may be caused by other teams, you can add @Ignore on the test method to avoid certain tests that are liable to break the entire build process.
Timeout Test
These are really interesting annotations. To test the performance of a method, we can create timeout tests which will blink RED if a method doesn’t complete in specified time.
Here is how this can be done:
package com.discoversdk;
import org.junit.Test;
public class DiscoverTimeoutTest {
//This test will always fail
@Test(timeout = 1000)
public void infinity() {
while (true) ;
}
//This test can't run more than 5 seconds, else failed
@Test(timeout = 5000)
public void testSlowMethod() {
//...
}
}
This is hard because once performance based timeout tests are written, they ensure that a method always returns in specified time period, otherwise, continuous integration frameworks like Jenkins will report a failed build!
JUnit Rule
Junit Rules work on the principle of AOP (aspect oriented programming). They intercept the test method thus providing an opportunity to do some stuff before or after the execution of a particular test method.
Take the example of the below code:
public class JunitRuleTest {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
@Test
public void testRule() throws IOException {
File newFolder = tempFolder.newFolder("Temp Folder");
assertTrue(newFolder.exists());
}
}
Every time the above test method is executed, a temporary folder is created and it gets deleted after the execution of the method. This is an example of an out-of-box rule provided by Junit.
Similar behaviour can also be achieved by creating our own rules. Junit provides the TestRule interface, which can be implemented to create our own Junit Rule.
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment