Saturday, June 28, 2008

Junit 3

Do You Need to Write a Test Class for Every Class That Need to Be Tested?

This is a simple question. But the answer shows your organization skills.

The technical answer is no. There is no need to write one test class for each every class that need to be tested. One test class can contain many tests for many test target classes.

But the practical answer is yes. You should design one test class per test target class for low level basic tests. This makes your test classes much easier to manage and maintain.

You should write separate test classes for high level tests that requires multiple target classes working together.


What Is JUnit TestCase?

JUnit TestCase is the base class, junit.framework.TestCase, used in JUnit 3.8 that allows you to create a test case. TestCase class is no longer supported in JUnit 4.4.

A test case defines the fixture to run multiple tests. To define a test case

  • Implement a subclass of TestCase
  • Define instance variables that store the state of the fixture
  • Initialize the fixture state by overriding setUp
  • Clean-up after a test by overriding tearDown

Each test runs in its own fixture so there can be no side effects among test runs. Here is an example:

import junit.framework.*;
// by FYICenter.com
public class MathTest extends TestCase {
protected double fValue1;
protected double fValue2;

protected void setUp() {
fValue1= 2.0;
fValue2= 3.0;
}

public void testAdd() {
double result= fValue1 + fValue2;
assertTrue(result == 5.0);
}

What Is JUnit TestSuite?

JUnit TestSuite is a container class, junit.framework.TestSuite, used in JUnit 3.8 that allows you to group multiple test cases into a collection and run them together. TestSuite class is no longer supported in JUnit 4.4.

Each test runs in its own fixture so there can be no side effects among test runs. Here is an example:

import junit.framework.*; // by FYICenter.com public class RunTestSuite { public static void main(String[] a) { TestSuite suite = new TestSuite(MathTest.class); TestResult result = new TestResult(); suite.run(result); System.out.println("Was it successful? " +result.wasSuccessful()); System.out.println("How many tests were there? " +result.runCount()); } }

How To Write a Single Class to Define Multiple Tests and Run Them?

Here is the answer for JUnit 3.8:

   1. Define a subclass of junit.framework.TestCase.
2. Override the setUp() and tearDown() methods.
3. Define multiple "public void testXXX()" methods.
One for each test.
A test method name must be started with "test".
Call the methods of tested object.
Check the expected results with an assertXXX() method.
4. Define a "public static void main()" method to run tests.
Call "junit.textui.TestRunner.run()" to run all tests.

Here is a nice example:

import junit.framework.*;
import junit.textui.*;
// by FYICenter.com
public class MathTestAndRun extends TestCase {
protected double fValue1;
protected double fValue2;

public static void main(String[] a){
junit.textui.TestRunner.run(MathTestAndRun.class);
}

protected void setUp() {
fValue1= 2.0;
fValue2= 3.0;
}

public void testAdd() {
double result= fValue1 + fValue2;
assertTrue(result == 5.0);
}

public void testMultiply() {
double result= fValue1 * fValue2;
assertTrue(result == 6.0);
}
}
}

How to Run Your JUnit 4.4 Tests with a JUnit 3.8 Runner?

I am not sure why you have to do this. But if you want to, you can use the junit.framework.JUnit4TestAdapter class included in JUnit 4.4 JAR file. Here is sample code:

import junit.framework.Test;
import junit.textui.TestRunner;
import junit.framework.JUnit4TestAdapter;
// by FYICenter.com

public class JUnit3Adapter {
public static void main (String[] args) {
Test adaptedTest = new JUnit4TestAdapter(HelloTest.class);
TestRunner.run(adaptedTest);
}
}

Classes junit.framework.Test, junit.textui.TestRunner and junit.framework.JUnit4TestAdapter are included in the JUnit 4.4 JAR file. You don't need to include the JUnit 3.8 JAR file in your CLASSPATH.

How Do You Test a "private" Method?

When a method is declared as "private", it can only be accessed within the same class. So there is no way to test a "private" method of a target class from any test class.

To resolve this problem, you have to perform unit testing manually. Or you have to change your method from "private" to "protected".


Can You Write a JUnit Test Case Class in 10 Minutes?

Here is a good JUnit test case class example that you should be able to write within 10 minutes. This example is provided by Varun Chopra and valid for JUnit 3.8.

Assuming that Java class DirLister.java has following three methods and one variable:

  • String dirPath - Member variable that represents directory in use
  • createLogFile(String fileName) - Member method that creates a file in dirPath
  • exists(String fileName) - Member method that checks the existence of a file within dirPath
  • getChildList() - Member method that gets the list of files and directories within dirPath

We want to test above three methods with Junit. For that, we will create another Java class that will have the test code for these three methods. See the class below:

/*
* DirListerTest.java
* JUnit based test
*
* Created on May 26, 2005, 11:33 AM
*/
package javaegs.junit;

import java.io.File;
import java.io.IOException;
import junit.framework.*;
/**
* @author varunc
*/

public class DirListerTest extends TestCase {

DirLister dl;

/**
* Test of createLogFile method, of class
javaegs.junit.DirLister.
*/

public void testCreateLogFile() {
dl = new DirLister("D:/temp/junittestdir");
dl.createLogFile("logFile.log");
assertTrue("File does not exist",dl.exists("logFile.log"));
}

/**
* Test of exists method, of class
javaegs.junit.DirLister.
*/

public void testExists() {
dl = new DirLister("D:/temp/junittestdir");
assertTrue("File does not exist",dl.exists("logFile.log"));
}

/**
* Test of getChildList method, of class
javaegs.junit.DirLister.
*/

public void testGetChildList() {
dl = new DirLister("D:/temp/junittestdir");
String[] files = null;
try {
files = dl.getChildList();
} catch(Exception ex) {
fail("Exception occured"+ex);
ex.printStackTrace();
}
assertNotNull("Children can't be null",files);
assertTrue("No. of files can't be 0",files.length>0);
}

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
junit.textui.TestRunner.run(DirListerTest.class);
}
}

Can You Explain a Sample JUnit Test Case Class?

Assuming that you have been give the sample JUnit test case class listed in the previous question, you should be able to provide the following notes:

DirListerTest class imports classes in junit.framework package. junit.framework package contains all common classes required to create junit test cases.

DirListerTest class extends junit.framework.TestCase class. TestCase class further extends junit.framework.Assert class which contains various assertXXX() methods required to created test cases. Therefore to use Junit framework, test class must extend TestCase class.

Corresponding to each method to be tested there is a method in the test class namely: testCreateLogFile(), testExists(), and testGetChildList(). You can name them anything but the starting word must be test.

One important point I would like to mention here is that I created three different methods to demonstrate three different and common scenarios while programming.

  • exists() method of DirLister returns a value. Our test case will demonstrate how to test such methods, which return a value.
  • createLogFile() returns nothing. It even catches the exception and ignores it (in one sense). Therefore, we won?t get the exception even if that method fails. Our test case will demonstrate how to go about such methods.
  • getChildList() returns a value as well as may throw exception. We will see how to handle this using Junit framework.

Let's now look inside the second test case method testExists():

public void testExists() {
dl = new DirLister("D:/temp/junittestdir");
assertTrue("File does not exist",dl.exists("logFile.log"));
}

First statement in it instantiates the DirLister object and points it to D:/temp/junittestdir directory. Next statement is what we need to focus on:

  assertTrue("File does not exist",dl.exists("logFile.log"));

assertTrue() is a standard method provided by Junit framework (there are many other assert methods also). It takes two arguments: a string message and a boolean condition.

If boolean condition is true, test passes and string message is ignored. Otherwise test fails and string message is displayed. In this case, if dl.exists("logFile.log") passes, test passes, otherwise test fails.

There is another variant of assertTrue() method provided by Junit that takes only one argument - the condition to check. This is true for all assertXXX methods provided by Junit.

Now you may say, that was a boolean result. What if a non-boolean value is returned? Okay, so Junit provides alternatives. We can use assertEquals() as below:

  assertEquals("not equal",dl.exists("logFile.log"), true);

Here first argument is the message that is displayed in case test fails. Second argument is the actual value. Normally we will call the method to be tested here and whatever value that method will return, will become the actual value. Third argument is the value we expect. So here, we expect it to be ?true?, hence we put true there.

There are various overloaded forms of assertEquals() method that can handle every result (from byte to String to Object).

Let's now look inside the first test case method testCreateLogFile(). If a method returns nothing and throws no exception, either it is not doing anything or is creating a side effect. createLogFile() is one such method. To test such methods, we will have to test the side effect they created. That's what testCreateLogFile() does.

public void testCreateLogFile() {
dl = new DirLister("D:/temp/junittestdir");
dl.createLogFile("logFile.log");
assertTrue("File does not exist",dl.exists("logFile.log"));
}

We expect that dl.createLogFile("logFile.log") should create logFile.log in directory in use. Therefore, in next statement we check the existence of such a file.

 assertTrue("File does not exist",dl.exists("logFile.log"));

If file will be there, test case will pass, otherwise it will fail.

Let's now look inside the other test case method testGetChildList(). getChildList() method may throw an exception. Therefore, we will have to call it in try block.

 public void testGetChildList() {
dl = new DirLister("D:/temp/junittestdir");
String[] files = null;
try {
files = dl.getChildList();
} catch(Exception ex) {
fail("Exception occured"+ex);
ex.printStackTrace();
}
assertNotNull("Children can't be null",files);
assertTrue("No. of files can't be 0",files.length>0);
}

If we don't expect getChildList() to throw exception for the directory value we provide, we can put in catch block a call to fail() method (as we have done).

fail() is again a standard method provided by Junit framework (belongs to junit.framework.Assert class). It fails a test case, which means it will show in the output that test case was failed and show the message we provided as argument to it. It has a without-argument variant also.

One more thing fail() will do is to exit the method. So assert statements following catch block will not be executed if fail() is called. That's logical since you should handle one bug at a time in unit testing.

Okay, so what if getChildList() succeeds. If we know that directory we have provided has at least one file in it, then:

    assertNotNull("Children can't be null",files);

will be useful. It will check that "files" variable is not null. If this test fails, it will display the message provided by us.

    assertTrue("No. of files can't be 0",files.length>0);

will further make it certain that number of files is greater than 0.

If we know the number of files within the directory we can do something like this:

  assertEquals(?Not equal?, files.length, n);

where n is the number of files.


Do You Need to Write a main() Method in a JUnit Test Case Class?

The right answer to this question is "No". But many developers do write a main() method in a JUnit test case class to call a JUnit test runner to run all tests defined in this class. This is not recommended, because you can always call a JUnit runner to run a test case class as a system command.

If you want to know how to call a JUnit runner in a main() method, you can this code included in the sample test case class listed in the previous question. This code is provided by Varun Chopra and valid for JUnit 3.8.

public static void main(String[] args) {
junit.textui.TestRunner.run(DirListerTest.class);
}

junit.textui.TestRunner.run() method takes test class name as argument. Using reflection, this method finds all class methods whose name starts with test. So it will find following 3 methods:

  testCreateLogFile()
testExists()
testGetChildList()

It will execute each of the 3 methods in unpredictable sequence (hence test case methods should be independent of each other) and give the result in console. Result will be something like this:

Time: 0.016
OK (3 tests)

In case you want to see the output in a GUI, you just need to replace statement in main method with following:

    junit.swingui.TestRunner.run(DirListerTest.class);

This will open up a nice swing based UI, which will show a green progress bar to show the status.


Can You Explain the Life Cycle of a JUnit 4.4 Test Class?

A JUnit 4.4 test class contains a @Before method, an @After method and multiple @test methods. When calling a test runner to run this test class, the runner will execute those methods in a specific order giving the test class an execution life cycle like this:

@Before
@Test XXX1
@After

@Before
@Test XXX2
@After

@Before
@Test XXX3
@After

What Is a JUnit Test Fixture?

A test fixture is a fixed state of a set of objects used as a baseline for running tests. The purpose of a test fixture is to ensure that there is a well known and fixed environment in which tests are run so that results are repeatable. Examples of fixtures:

  • Loading a database with a specific, known set of data
  • Copying a specific known set of files
  • Preparation of input data and setup/creation of fake or mock objects

In other word, creating a test fixture is to create a set of objects initialized to certain states.

If a group of tests requires diferent test fixtures, you can write code inside the test method to create its own test fixture.

If a group of tests shares the same fixtures, you should write a separate setup code to create the common test fixture.





How Do You Test an Expected Exception with JUnit?

If you want to test a method that will throw an exception under a specific condition, you can:

  • Put the test code inside a "try" block".
  • Catch the expected exception object.
  • Assert the exception object with Assert.assertNotNull().

JUnit runner will fail this test if the test code did not raise the expected exception.

Here is a good test class that test the expected IndexOutOfBoundsException exception raised by the get() method of the ArrayList class:

import org.junit.*;
import java.util.*;

// by FYICenter.com
public class ExpectedExceptionTest1 {
@Test public void outOfBounds() {
ArrayList emptyList = new ArrayList(); Exception eOutOfBounds = null; // catch the expected exception try { Object o = emptyList.get(1); } catch (IndexOutOfBoundsException e) { eOutOfBounds = e; } // asset the exception object Assert.assertNotNull("No expected exception", eOutOfBounds); } }

But there a better way to test the expected exception provided by the JUnit @Test annotation. See the next question.


No comments:

Topics