BLOGS

Product Engineering Outsourcing, Tech Talk

10
Aug

Using Customized Junit TestSuite for testing

Do developers also have to test? How do developers test since they are supposed to be good at coding and not at testing? Well, then the solution is for them to code the test as well!

This blog covers the basics of Junit – a framework designed to write repeatable tests. It is based on the xUnit architecture, which is basically a collection of code-driven testing techniques, allowing testing of various software units such as functions and classes and eliminating the need to write the same tests multiple times.

JUnit is simple to use and write TestCases (written as one word and different from a ‘test case’) that could cover almost all testing scenarios if planned well and covered by the tests. Moreover, it plays a vital role in code maintenance and reduces regression testing usually added by new features or code fixes.

So how do we start about JUniting? Well, the simplest rule of thumb to follow is to plan the input and output for any given scenario, be it a positive scenario or a negative one, and then write a test involving each of these scenarios. This ensures that each condition is checked well before the code release. Moreover, whenever the test suite is run for regression checks, it identifies immediate issues, introduced either due to additional fixes or addition of new features. This may mean that either the new code broke some valid scenario, or the broken scenario is no longer valid and you may turn it into a positive scenario by tweaking that test.

JUnit is based on two key Java design patterns: Command pattern and Composite pattern. A TestCase constitutes the Command object whereas a TestSuite comprises Composite TestCases.

Writing JUnit code is very simple and we would not get into the basics of it in this blog. If you are using Eclipse or any other IDE, it supports creation of JUnit tests for a given class in just a few seconds. However, we shall look at ‘from the scratch’ approach wherein we want to add a customized test suite.

The prerequisite for this would be downloading JUnit 4.x and adding the junit-xxx.jar in your classpath.

Implementing a TestClass

A quick annotation preview for TestClass

A TestClass is implemented using the ‘@Test’ annotation. There is no need to extend the TestClass now with JUnit 4.x and the method names need not start with ‘test’ as required by earlier versions of JUnit.

The ‘@BeforeClass’ annotated method is executed as soon as the TestClass is initialized and the ‘@AfterClass’ annotated method is executed before the TestClass is being finalized. They run only once in the lifetime of the tests.

The ‘@Before’ annotated method is executed before each test is run and the ‘@After’ annotated method is executed after each test has completed execution.

The ‘@Ignore’ annotation is used to ignore the test for execution.

The ‘@Timeout’ annotation takes the time in milliseconds as a parameter and fails if the test does not complete execution within the stipulated time.

A simple TestCase example is given next.

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import test.com.xoriant.testsuite.TestFinalizer;
import test.com.xoriant.testsuite.TestInitializer;

import com.xoriant.SingletonClass;

public class SingletonClassTest
{
    /**
     * @throws java.lang.Exception
     */
    @BeforeClass
    public static void setup() throws Exception
    {
        if (System.getProperty(TestInitializer.TEST_INITIALIZED) == null)
        {
            TestInitializer.setUp();
        }
    }

    @Test
    public final void testGetInstance()
    {
	try
      {
        SingletonClass singletonClass1 = SingletonClass.getInstance();
        SingletonClass singletonClass2 = SingletonClass.getInstance();

        assertEquals(singletonClass1, singletonClass2);
      }
	catch(Exception e)
{
	  fail("Execution failed due to: " + e.toString());
}
    }

    @AfterClass
    public static void tearDown()
    {
        if (System.getProperty(TestFinalizer.TEST_FINALIZED) == null)
        {
            TestFinalizer.tearDown();
        }
    }
}

Implementation of the TestInitializer class

This is a common class utilized by the test cases for loading test data for the tests prior to any execution of the tests. This class may also be invoked for independent test cases.

import org.junit.BeforeClass;
import org.junit.Test;

import static org.junit.Assert.assertTrue;

public class TestInitializer
{
    public static final String TEST_INITIALIZED = "com.api.testsuite.initialized";

    static
    {
        logger.debug("Setting system property...");
        System.setProperty(PropertyManager.BUNDLE_PROPERTY_NAME, "test_config");
        System.setProperty(TEST_INITIALIZED, "Y");
    }

    /**
     * Sets up the test
     */
    @BeforeClass
    public static void setUp() throws Exception
    {
	  // Initialize the data required for the tests
    }

    @Test
    public void dummyTest()
    {
        assertTrue(true);
    }
}

Implementation of the TestFinalizer class

This class is a common class for removing test data after all the tests in the TestSuite have completed. This class may also be invoked for independent test cases.

import org.junit.AfterClass;
import org.junit.Test;
import static org.junit.Assert.assertTrue;

public class TestFinalizer
{
    public static final String TEST_FINALIZED = "com.api.testsuite.finalized";

    static
    {
        System.setProperty(PropertyManager.BUNDLE_PROPERTY_NAME, "test_config");
        System.setProperty(TEST_FINALIZED, "Y");
    }

    @AfterClass
    public static void tearDown()
    {
          // Remove the test data that was required for the tests
    }

    @Test
    public void dummyTest()
    {
        assertTrue(true);
    }
}

Implementing a TestSuite

A TestSuite is implemented using the ‘@SuiteClasses’ and ‘@RunWith’ annotations. It takes a list of TestClasses as a class array parameter and executes each of them in that order.

Here, we initialize the TestSuite by adding required test data into the database in the TestInitializer class. Similarly, we cleanup the data using the TestFinalizer class. The ‘@RunWith‘ annotation allows us to define a customized TestSuite with listeners that get triggered at appropriate events in the test execution cycle.

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

import test.com.xoriant.SingletonClassTest;

@RunWith(CustomSuite.class)
@SuiteClasses( { TestInitializer.class, SingletonClassTest.class, TestFinalizer.class })

public class AllTests
{
}

Implementing a custom TestSuite

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

import test.com.xoriant.SingletonClassTest;

@RunWith(CustomSuite.class)
@SuiteClasses( { TestInitializer.class, SingletonClassTest.class, TestFinalizer.class })

public class AllTests
{
}

Some of the best practices for JUnit tests that should be followed

  • Do not run tests with interdependent test data. This could create maintenance problems later on.
  • The source folders of the actual classes and the test classes should be different so that the tests are separate from the original source files and do not need to be shipped along with production code.
  • Keep the test class packages same as the class in question to be tested. This allows protected methods to be tested.
  • JUnit can be integrated with nightly builds and executed as an ant task. This would enable running the tests on a daily basis and avoiding production problems.
  • Utilize proper initialization and destruction methods. Never initialize test data in constructors.
  • Keep tests up-to-date to avoid revisiting the tests to be fixed.

Summarizing, in this blog, we saw how to leverage the JUnit testing framework and its derivative API TestCase to increase the testing efficiency for Java based projects.

It is to be noted that apart from Java, JUnit has also been ported for several other popular programming languages such as C#, C++, Perl, PHP and many others. The ultimate objective of using JUnit is to test before you code so that you can rest after you code!


Anand Ved
About

Technical Lead – Cloud Computing Project

2 comments on “Using Customized Junit TestSuite for testing
  1. gopal says:

    Hi nice article,
    I don’t understand the try catch fail in the test method @Test public void testGetInstance(){…}
    junit will report the correct message without the try catch as well.

  2. Anand Ved Anand Ved says:

    Yes, no need of try-catch block here, In case it may be needed to allow handling any application specific exception that getInstance method of the SingletonClass may throw.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>