Pages

April 24, 2017

All about JUnit..

What is JUnit and Unit Test Case?
JUnit is a testing framework for unit testing. It uses Java as a programming platform, and it is an Open Source Software managed by the JUnit.org community.

Unit Test Case is a part of the code that ensures that the another part of code (method) behaves as expected. For each requirement, there must be at least two test cases one negative test and one positive test.


First JUnit test case:

1). Create a java class file name TestJunit, which has a method testName(). This method will compare whether 'name' is valid or not

import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class TestJunit {
   @Test   
   public void testName() {
      String name = "Name is K Himaanshu Shuklaa";
      assertEquals("Name is K Himaanshu Shuklaa",name);
   }
}


2). Create a java class file name TestRunner.
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;

public class TestRunner {
   public static void main(String[] args) {
      Result result = JUnitCore.runClasses(TestJunit.class);       
      for (Failure failure : result.getFailures()) {
         System.out.println("failure result="+failure.toString());
      }       
      System.out.println("is test successful? "+result.wasSuccessful());
   }
}


When you run TestRunner, it will call testName() method of TestJunit. If the 'name' is equal to the value passed in assertEquals, then output will be:
"is test successful? true"

But suppose, the value of 'name' variable is 'Name is Tiger Shroff', then output will be :
failure result=testName(com.junit.TestJunit): expected: < Name is [K Himaanshu Shuklaa] > but was: < Name is [Tiger Shroff] >
is test successful? false


Features of JUnit Test Framework : JUnit test framework provides the following important features :-

1). Fixture : A test fixture is a fixed state in code which is tested used as input for a test. Another way to describe this is a test precondition. For example, a test fixture might be a a fixed string, which is used as input for a method. The test would validate if the method behaves correctly with this input

It includes :
  • setUp() method, which runs before every test invocation.
  • tearDown() method, which runs after every test method.

e.g: Let us make some changes in TestJunit, we need to extend TestCase class and add two methods setUp() and tearDown().
import junit.framework.TestCase;
import org.junit.Test;
public class TestJunit extends TestCase{
    protected String name;
    protected void setUp() {
        System.out.println("Inside setUp()");
        name = "Name is Tiger Shroff";
    }
    protected void tearDown() {
        System.out.println("Inside tearDown()");
    }
    @Test
    public void testName() {
        System.out.println("Inside testName()");
        assertEquals("Name is K Himaanshu Shuklaa", name);
    }
}


Now when you run TestRunner, it will give an output (test is failed because the 'name' is nt equal to the value passed in assertEquals):
Inside setUp()
Inside testName()
Inside tearDown()
failure result=testName(com.junit.TestJunit): expected: < Name is [K Himaanshu Shuklaa]> but was: < Name is [Tiger Shroff] >
is test successful? false


2). Test Suites : If you have several test classes, you can combine them into a test suite. Running a test suite executes all test classes in that suite in the specified order. A test suite can also contain other test suites.

In JUnit, both @RunWith and @Suite annotation are used to run the suite test. Given below is an example that uses TestJunit1 & TestJunit2 test classes.

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
                TestJunit1.class,
                TestJunit2.class })

public class TestRunner {

}

3). Test Runners : It is used for executing the test cases.
4). JUnit Classes : They are important classes, used in writing and testing JUnits. Some of the important classes are −
  • Assert − Contains a set of assert methods.
  • TestCase − Contains a test case that defines the fixture to run multiple tests.
  • TestResult − Contains methods to collect the results of executing a test case.
junit.framework : Its the most important package in JUnit because it contains all the core classes.
  • Assert : A set of assert methods.
  • TestCase : A test case defines the fixture to run multiple tests.
  • TestResult : A TestResult collects the results of executing a test case.
  • TestSuite : A TestSuite is a composite of tests.

Assert Class : It is present in org.junit package of junit.framework. Assert class provides a set of assertion methods useful for writing tests. Only failed assertions are recorded.

A few methods to assert test results (parameters in [] brackets are optional and of type String):
fail(message) : Let the method fail. Might be used to check that a certain part of the code is not reached or to have a failing test before the test code is implemented. The message parameter is optional.
  • assertTrue([message,] boolean condition) : Checks that the boolean condition is true.
  • assertFalse([message,] boolean condition) : Checks that the boolean condition is false.
  • assertEquals([message,] expected, actual) : Tests that two values are the same. Note: for arrays the reference is checked not the content of the arrays.
  • assertEquals([message,] expected, actual, tolerance) : Test that float or double values match. The tolerance is the number of decimals which must be the same.
  • assertNull([message,] object) : Checks that the object is null.
  • assertNotNull([message,] object) : Checks that the object is not null.
  • assertSame([message,] expected, actual) : Checks that both variables refer to the same object.
  • assertNotSame([message,] expected, actual) : Checks that both variables refer to different objects

TestCase Class : It is present in org.junit package of junit.framework, this class extends Assert and implements Test interface. Some of the important methods of TestCase class are :
  • int countTestCases() Counts the number of test cases executed by run(TestResult result).
  • TestResult createResult() Creates a default TestResult object.
  • String getName() Gets the name of a TestCase.
  • TestResult run() A convenience method to run this test, collecting the results with a default TestResult object.
  • void run(TestResult result) Runs the test case and collects the results in TestResult.
  • void setName(String name) Sets the name of a TestCase.
  • void setUp() Sets up the fixture, for example, open a network connection.
  • void tearDown() Tears down the fixture, for example, close a network connection.
  • String toString() Returns a string representation of the test case.

TestResult Class : It is present in org.junit package of junit.framework. A TestResult collects the results of executing a test case. It is an instance of the Collecting Parameter pattern. The test framework distinguishes between failures and errors. A failure is anticipated and checked for with assertions. Errors are unanticipated problems like an ArrayIndexOutOfBoundsException. Some of the important methods of TestResult class are :
  • void addError(Test test, Throwable t) Adds an error to the list of errors.
  • void addFailure(Test test, AssertionFailedError t) Adds a failure to the list of failures.
  • void endTest(Test test) Informs the result that a test was completed.
  • int errorCount() Gets the number of detected errors.
  • Enumeration errors() Returns an Enumeration for the errors.
  • int failureCount() Gets the number of detected failures.
  • void run(TestCase test) Runs a TestCase.
  • int runCount() Gets the number of run tests.
  • void startTest(Test test) Informs the result that a test will be started.
  • void stop() Marks that the test run should stop.
What is the difference between errors and failures?
failures occurs when your test cases fail,  i.e. your assertions are incorrect. Errors are unexpected errors that occur while trying to actually run the test - exceptions, etc.

TestSuite : It is a Composite of tests. It runs a collection of test cases. Some of the important methods of TestSuite class are:
  • void addTest(Test test) Adds a test to the suite.
  • void addTestSuite(Class testClass) Adds the tests from the given class to the suite.
  • int countTestCases() Counts the number of test cases that will be run by this test.
  • String getName() Returns the name of the suite.
  • void run(TestResult result) Runs the tests and collects their result in a TestResult.
  • void setName(String name) Sets the name of the suite.
  • Test testAt(int index) Returns the test at the given index.
  • int testCount() Returns the number of tests in this suite.
  • static Test warning(String message) Returns a test which will fail and log a warning message.
Assertion in JUnit
All the assertions are declared inside Assert class. This class provides a set of assertion methods, useful for writing tests. Only failed assertions are recorded. Some of the important methods of Assert class are:
  • void assertEquals(boolean expected, boolean actual) : Checks that two primitives/objects are equal.
  • void assertTrue(boolean expected, boolean actual) : Checks that a condition is true.
  • void assertFalse(boolean condition) : Checks that a condition is false.
  • void assertNotNull(Object object) : Checks that an object isn't null.
  • void assertNull(Object object) : Checks that an object is null.
  • void assertSame(boolean condition) : The assertSame() method tests if two object references point to the same object.
  • void assertNotSame(boolean condition) : The assertNotSame() method tests if two object references do not point to the same object.
  • void assertArrayEquals(expectedArray, resultArray): It  will test whether two arrays are equal to each other.
Annotation in JUnit:
  • @Test : The Test annotation tells JUnit that the public void method to which it is attached can be run as a test case.
  • @Before : Several tests need similar objects created before they can run. Annotating a public void method with @Before causes that method to be run before each Test method.
  • @After : If you allocate external resources in a Before method, you need to release them after the test runs. Annotating a public void method with @After causes that method to be run after the Test method.
  • @BeforeClass : Annotating a public static void method with @BeforeClass causes it to be run once before any of the test methods in the class.
  • @AfterClass : This will perform the method after all tests have finished. This can be used to perform clean-up activities.
  • @Ignore : The Ignore annotation is used to ignore the test and that test will not be executed.
Call sequence:
  • First of all, the beforeClass() method executes only once.
  • The afterClass() method executes only once.
  • The before() method executes for each test case, but before executing the test case.
  • The after() method executes for each test case, but after the execution of test case.
  • In between before() and after(), each test case executes.
Ignore Test: Sometimes it so happens that our code is not completely ready while running a test case. As a result, the test case fails. The @Ignore annotation helps in this scenario.
A test method annotated with @Ignore will not be executed.
If a test class is annotated with @Ignore, then none of its test methods will be executed.

Time Test: If a test case takes more time than the specified number of milliseconds, then JUnit will automatically mark it as failed. The timeout parameter is used along with @Test annotation.

e.g: if testName() takes more than 1000 milliseconds, then JUnit will mark it as failed.

@Test(timeout = 1000)  
public void testName() {
   String name = "Name is K Himaanshu Shuklaa";
   assertEquals("Name is K Himaanshu Shuklaa",name);
}


Exceptions Test: JUnit provides an option of tracing the exception handling of code. You can test whether the code throws a desired exception or not. The expected parameter is used along with @Test annotation

@Test(expected = ArithmeticException.class)
public void testName() {
   String name = "Name is K Himaanshu Shuklaa";
   assertEquals("Name is K Himaanshu Shuklaa",name);
}

Parameterized Test: It is introduced in JUnit 4. Parameterized tests allow a developer to run the same test over and over again using different values.

There are five steps that we need to follow to create a parameterized test:
  1. Annotate test class with @RunWith(Parameterized.class).
  2. Create a public static method annotated with @Parameters that returns a Collection of Objects (as Array) as test data set.
  3. Create a public constructor that takes in what is equivalent to one "row" of test data.
  4. Create an instance variable for each "column" of test data.
  5. Create your test case(s) using the instance variables as the source of the test data.

1).
public class PrimeNumberChecker {
    public Boolean validate(final Integer primeNumber) {
        for (int i = 2; i < (primeNumber / 2); i++) {
            if (primeNumber % i == 0) {
                return false;
            }
        }
        return true;
    }
}


2).
import java.util.Arrays;
import java.util.Collection;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import static org.junit.Assert.assertEquals;
@RunWith(Parameterized.class)
public class PrimeNumberCheckerTest {

    private Integer inputNumber;
    private Boolean expectedResult;
    private PrimeNumberChecker primeNumberChecker;
   
    @Before
    public void initilize()
    {
        primeNumberChecker=new PrimeNumberChecker();
    }
//public PrimeNumberCheckerTest(){}
    public PrimeNumberCheckerTest(Integer inputNumber, Boolean expectedResult) {
        this.inputNumber = inputNumber;
        this.expectedResult = expectedResult;
    }
     @Parameterized.Parameters
    public static Collection primeNumbers() {
        return Arrays.asList(new Object[][] {
                { 2, true }, { 6, false },
                { 8, true }, { 22, false },
                { 23, true } });
    }   
    @Test
    public void testPrimeNumberChecker() {
        System.out.println("Parameterized Number is : " + inputNumber);
        assertEquals(expectedResult, primeNumberChecker.validate(inputNumber));
    }
}


3).
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;

public class TestRunner {
   public static void main(String[] args) {
      Result result = JUnitCore.runClasses(PrimeNumberCheckerTest.class);

      for (Failure failure : result.getFailures()) {
         System.out.println("***Inside TestRunner: failure result="+failure.toString());
      }
       
      System.out.println("***Inside TestRunner: is test successful?"+result.wasSuccessful());
   }
}

Output of TestRunner:
Parameterized Number is : 2
Parameterized Number is : 6
Parameterized Number is : 8
Parameterized Number is : 22
Parameterized Number is : 23
***Inside TestRunner: failure result=expected: but was:
***Inside TestRunner: is test successful?false



If you do not mention @RunWith(Parameterized.class) in PrimeNumberCheckerTest, then below exception will be thrown:
***Inside TestRunner: failure result=initializationError(com.parameter.PrimeNumberCheckerTest): Test class should have exactly one public zero-argument constructor

If you mention @RunWith(Parameterized.class), but forgot to add @Parameterized.Parameters in primeNumber() method of PrimeNumberCheckerTest, then below exception will occur
***Inside TestRunner: failure result=initializationError(com.parameter.PrimeNumberCheckerTest): No public static parameters method on class com.parameter.PrimeNumberCheckerTest


-K Himaanshu Shuklaa..

No comments:

Post a Comment