JUnit 4 Testing Framework by Peter Froehlich & Joanne Selinski WHY: JUnit is a testing framework that enables us to create specific unit tests for methods in a Java class. This is particularly useful in testing multiple different implementations of an ADT (interface). So why use a testing framework like JUnit 4 instead of writing the a "main" method with the test code in it? For one thing, JUnit allows you to modularize your tests better. It's not uncommon for large software projects to have just as much testing code as actual program code, and so the principles you use to make regular code easier to read (splitting things into methods and classes, etc.) should also apply to test code. Also, JUnit allows you to run all your test cases every time, it doesn't stop at the first failing test case like "assert" does. This way you can get feedback about multiple failed tests all at once. Finally, lots of companies expect graduates to have some experience with testing frameworks, so why not pick it up now? Note that testing is not just for software developers anymore, increasingly people working with software developers but who are themselves not software developers will be asked to contribute to testing a certain application being developed by their company. So it's a really useful skill to have on your list. WHAT: So how do we write a test case for JUnit 4? As opposed to earlier versions of JUnit, JUnit 4 uses Java's annotation mechanism which allows us to "mark" certain methods as test cases. See http://docs.oracle.com/javase/tutorial/java/javaOO/annotations.html for details on the general mechanism. For JUnit 4 we import the "Test" annotation from the framework by saying import org.junit.Test; and then write our test methods using that annotation: @Test public void testThisAndThat() { ... } Any method "tagged" with the "@Test" annotation will be run by JUnit 4 as a separate test case. You can have as many of these as you want, and you should write a separate method for each test case. (If you put multiple test cases in one method, JUnit cannot tell you exactly which one failed!) You should also name these very specifically to describe exactly what is being tested. There are many more annotations you can use. For example, the "@Before" and "@After" annotations mark methods that should run before and after each individual test case. Similarly, the "@BeforeClass" and "@AfterClass" annotations mark methods that should run once before or once after all test cases. Typically these are used for initializing objects that will then be tested. Finally, you can use the "@Test(expected=SomeException.class)" annotation to check for a particular exception that you expect. Recall that you not only want to test the "good" uses of your data structures, but you also want to make sure that the "bad" uses trigger the correct exceptions. Within the test cases we use different types of assertions to describe the expected behavior of the objects being tested. You can find all of these in the JUnit documentation on the http://www.junit.org/ website, but assertEquals is the most commonly used one. Here are a few examples: assertEquals(expectedValue, actualValue); assertTrue(booleanExpression); // also assertFalse assertNotNull(someObject); // also assertNull The assertion methods are also overloaded with a version that has a first parameter string to detail the cause of an error in the case that the assertion fails. This is strongly recommended to give valuable feedback to the junit user when running the tests. assertEquals("detailed error message if fails", expectedValue, actualValue); assertFalse("something going on", booleanExpression); All of the test annotation types (Test, Before, BeforeClass, etc.) and assertion types (assertEquals, assertTrue, assertNotNull, etc.) must be imported before they can be used. Here is an example of several of these annotations to write a few test cases for a SimpleArray abstraction: import org.junit.Test; import org.junit.BeforeClass; import static org.junit.Assert.assertEquals; public class TestSimpleArray { static Array shortArray; @BeforeClass public static void setupArray() throws InvalidLengthException { shortArray = new SimpleArray(10, "Bla"); } @Test public void newArrayLengthGood() throws InvalidLengthException { assertEquals("Length inconsistent", 10, shortArray.length()); } @Test public void newArrayInitialized() throws InvalidLengthException, InvalidIndexException { for (int i = 0; i < shortArray.length(); i++) { assertEquals("Bla", shortArray.get(i)); } } @Test(expected=InvalidIndexException.class) public void invalidIndexDetected() throws InvalidIndexException { shortArray.set(shortArray.length(), "Paul"); } } Note that this set of test cases is far from complete, but it shows most of the features you'll need to write your test code from here on out. HOW (on the CS Linux servers): In order to use JUnit 4 you have to visit the http://www.junit.org/ website and download the file junit-4.12.jar that contains the Java class files for the framework (the framework is itself written in Java of course), and also another file hamcrest-core-1.3.jar. (Both are linked for convenience from our schedule for each download from the course website.) Put these jar files into the same directory all your code is in, or a parent folder (similar to what we did for checkstyle). (Note: older versions of junit did not require the extra hamcrest component.) A ".jar" file is really just a ".zip" archive with a specific structure that allows us to add more classes to the Java class library we already have. There are detailed, operating-system specific installation instructions on the http://www.junit.org/ website, but we wanted to show you a way of using the ".jar" that will work on all systems. Once you have test cases, you need to start the Java compiler and the Java Virtual Machine in a way that includes the new classes from the framework, we'll get to that below. Compiling and running the JUnit test cases is a bit of a hassle. For most IDEs there is a way to integrate JUnit with the development environment so that the following command line stunts are not necessary. In particular, Eclipse has a mechanism for working with JUnit testing that you could explore. If you prefer the command line, here's how we compile the test cases: javac -cp junit-4.12.jar:. TestArray.java The "-cp" option sets the "class path" for the Java compiler, meaning where it looks to find classes that you import in your code. By default the Java compiler looks in the system class library (the stuff that came with Java when you installed it) and in the current directory. We want it to look into the ".jar" file as well, so we have to specify a new class path. However, we also want the compiler to look into the current directory where all our code lives, so we specify "." as a second class path after the ":" symbol. Now you know all the dirty secrets about how this works, so you can forget them again and just use the line as given. :-D To run the test cases, we need to use something called a "test runner" from the JUnit framework. Here's how we do it from the command line: java -cp junit-4.12.jar:hamcrest-core-1.3.jar:. org.junit.runner.JUnitCore TestArray So what we're doing here is we run "org.junit.Runner.JUnitCore" as our main program, and we supply "TestArray" which is the class that our test cases are in as an argument to that main program. Voila, that runs our tests and tells us how many passed and how many failed. If you prefer to put the jar files in a parent folder (and then actual project folders in subdirectories), then from the project folders you would list the classpaths for the jars with "../" prepended to them, like this: javac -cp ../junit-4.12.jar:. TestArray.java java -cp ../junit-4.12.jar:../hamcrest-core-1.3.jar:. org.junit.runner.JUnitCore TestArray Note: if you are doing this via command-line on a windows machine, you must use a ';' instead of a ':' to separate the paths: javac -cp junit-4.12.jar;. -Xlint:all TestArray.java java -cp junit-4.12.jar;. org.junit.runner.JUnitCore TestArray If you're using Eclipse, doing JUnit testing is even easier. You can import a test file into your project, or create a test file. Then select that file, and choose Run As JUnit Test. This FAQ is a pretty good resource for understanding JUnit: http://junit.sourceforge.net/doc/faq/faq.htm