My favorites | Sign in
Logo
                
Search
for
Updated Jul 08, 2009 by uprime812
JUnitQuickTutorial  
A quick JUnit tutorial for beginners

JUnit 4.x Quick Tutorial

by Wishnu Prasetya

This tutorial is for novice Java programmers who try to learn JUnit.

How does JUnit work?

JUnit is a library packed in a jar file. Among other things it contains a tool (called test runner) to run your test files. It is not an automated testing tool: you still have to write your test files by hand. JUnit does give you some support so that you can write those test files more conveniently.

Suppose you have a class C that you want to test. We will write the tests in a new class; let’s call it Ctest. This Ctest is our test file. Actually, test class is a better name. We will typically group the tests in Ctest in a bunch of methods called test methods. You will see an example soon.

To actually test C we need to execute its test class Ctest. This is done by calling JUnit’s test runner tool; we pass the name Ctest to it. That’s all. JUnit will then execute Ctest for you.

JUnit will report how many of the test methods in Ctest succeed, and how many fail. The detail of each failure will be reported; this will be in the form of a print of Java’s stack trace leading to the location of the failure.

Locate it first

If you don’t have JUnit yet, you need to download it (from JUnit site). To use it you just need the jar file. A full download also contains its documentation.

Now locate where this jar file is; it is usually called:

junit-<version-number>.jar

To use JUnit you will need to add the full path to this jar to your class path. We will see examples latter.

A simple example

Let us consider the simple class below. This is the class that we want to test; but first let me explain a bit about what it is.

The class is called Subscription; an instance of it represents a subscription to something (e.g. newspaper, but it doesn’t really matter here). Each subscription has its total price, stored in the variable price. This price is in Euro-cent. It also has the length of the subscription, given in months.

public class Subscription {

   private int price ; // subscription total price in euro-cent
   private int length ; // length of subscription in months

   // constructor :
   public Subscription(int p, int n) {
     price = p ;
     length = n ;
   }

  /**
   * Calculate the monthly subscription price in euro,
   * rounded up to the nearest cent.
   */
   public double pricePerMonth() {
     double r = (double) price / (double) length ;
      return r ;
   }

  /**
   * Call this to cancel/nulify this subscription.
   */
   public void cancel() { length = 0 ; }

}

For example, new Subscription(1000,2) will create a new subscription of 1000 Euro-cent for the total period of 2 months.

By the way, the class has a number of bugs; e.g. pricePerMonth is supposed to return the price per month in euro. However it calculates the price in cent.

Let’s write a test

Let us write two simple tests to check if pricePerMonth correctly calculates the price per month:

  1. If we have a subscription of 200 cent for a period of 2 month, its monthly price should be 1 euro, right?
  2. The monthly price is supposed to be rounded up to the nearest cent. So, if we have a subscription of 200 cent for a period of 3 month, its monthly price should be 0:67 euro.

Of course we can write tests without JUnit; but this tutorial is about JUnit. So here is how we write them in JUnit.

Each of the tests above will be implemented as a test method; you then group your test methods in a test class. Usually you would group all the test methods that test a certain target class C is a test class called CTest, but you can also have multiple test classes if you want, and call them whatever you want.

Here is my test class implementing the above two tests:

import org.junit.* ;
import static org.junit.Assert.* ;

public class SubscriptionTest {

   @Test
   public void test_returnEuro() {
      System.out.println("Test if pricePerMonth returns Euro...") ;
      Subscription S = new Subscription(200,2) ;
      assertTrue(S.pricePerMonth() == 1.0) ;
   }

   @Test
   public void test_roundUp() {
      System.out.println("Test if pricePerMonth rounds up correctly...") ;
      Subscription S = new Subscription(200,3) ;
      assertTrue(S.pricePerMonth() == 0.67) ;
   }
}

The marker @Test is called an annotation in Java. When we later execute JUnit’s test runner it needs to know which methods in your test class are test methods (e.g. you may have several helper methods in your test class). The @Test is used to mark that a method is a test method.

In the first test (test_returnEuro) we first create a Subscription; we call it S. Then we want to check that S.pricePerMonth() will return the expected value of 1.0. The checking is done by the code:

assertTrue(S.pricePerMonth() == 1.0)

By the way, the annotation @Test and the method assertTrue are things exported by the JUnit library; so you need the imports as in the above code to use them.

Compiling and executing your test

Next we want to execute the above test class (SubscriptionTest). I’ll show how to do this from a console. If you use an IDE the steps are a bit different.

Open a console. You first need to compile the test class, and then you can execute it. The commands are (in Windows):

prompt> javac -cp .;<full path to JUnit.jar> SubscriptionTest.java

prompt> java  -cp .;<full path to JUnit.jar> org.junit.runner.JUnitCore SubscriptionTest

(In e.g. Mac you probably have to use ':' instead of ';')

The first command will compile the test class. The second will execute JUnit’s test runner; we pass to it the name of your test class.

This is what you will get from JUnit; it reports that both tests fail. Notice also that it reports which lines exactly in your test class fail.

Time: 0,015

There were 2 failures:

   1) test_returnsEuro(SubscriptionTest)
      java.lang.AssertionError:
      ...
      at SubscriptionTest.test_returns_Euro(SubscriptionTest.java:13)
      ...

   2) test_roundUp(SubscriptionTest)
      ...
      at SubscriptionTest.test_roundUp(SubscriptionTest.java:19)
      ...

Tests run: 2, Failures: 2

The next thing you want to do is to fix your Subscription class and then test it again. We keep fixing it until we get no more failures. You may in the mean time also want to add more tests to your test class.

What now!?

That’s it! Now go and write your own tests.

Further reading

END.


Comment by benrady, Nov 11, 2008

Why do you prefix your test methods with "test"? What if you used something more expressive?

Comment by uprime812, Nov 12, 2008

To benrady:

No reason really, other than it's an old habbit :) I suppose you can name it anything you want as long as it is still within Java's naming convention. So, it can be:

@Test   
public void test_if_pricePerMonth_returns_Euro() { ... }

which is much more informative. Is this what you mean by "more expressive"?

Comment by vinodv86, Nov 13, 2008

There seems to be a problem with the test_if_pricePerMonth_returns_Euro() method. Shouldn't it be asserting if S.pricePerMonth() is 1.0 since 200 cents for 2 months comes out to 100 cents per month which is 1.0 euro.

Comment by uprime812, Nov 14, 2008

You're right! Thanks!

Comment by sami.jan, Nov 28, 2008

dude, this is great!!!

java, it seems to me suffers from an inherent lack of decent help - windows has its charles petzold, python has its mark pilgrim ...

thanks

Comment by GeneDeLisa, Dec 03, 2008

Java names like test_if_pricePerMonth_returns_Euro() gives me Corba nightmares.

Discover Java naming conventions before publishing next time.

Comment by uprime812, Dec 04, 2008

Oh ok ... I'll revert it back to simple name, since it is a tutorial for starters.

For advanced testers, I don't completely agree with you. In a real situation I expect you to have lots of test methods. Java name can be arbitrary long, so that you can embed useful information in it, which you can recover via reflection. This can be useful for e.g. making global reports on your set of test methods.

Comment by rnideffer, Dec 10, 2008

He is not referring to the length of your method names. He is talking about Java conventions for the style of names, i.e:

TestIfPricePerMonthReturnsEuro?()

versus

test_if_pricePerMonth_returns_Euro()

Comment by uprime812, Dec 12, 2008

Oooh ... Ok. In that case I find the first rather ugly. At least my poor eyes will have to work twice as hard to auto-insert the spaces.

Comment by bill.tha...@tqs.com, Dec 17, 2008

I think your underscores are fine when describing an action. It gives it a little scheme feel which is comfortable.

Comment by arthurbuliva, Dec 17, 2008

Beautiful and straight-forward. Thanks! Passes for

public double pricePerMonth()

{
double r = Math.round(((double) price / (double) length) / 100); return r;
}

Sweet!

Comment by achourso, Dec 23, 2008

hi, i tried to run this example but i had a problem. When i tried to compile the test i took response: javac -cp /usr/share/java/junit4-4.3.1.jar SubscriptionTest?.java

SubscriptionTest?.java:12: cannot find symbol symbol : class Subscription location: class SubscriptionTest?

Subscription S = new Subscription(200,2) ; ^
SubscriptionTest?.java:12: cannot find symbol symbol : class Subscription location: class SubscriptionTest?
Subscription S = new Subscription(200,2) ;
^
SubscriptionTest?.java:19: cannot find symbol symbol : class Subscription location: class SubscriptionTest?
Subscription S = new Subscription(200,3) ; ^
SubscriptionTest?.java:19: cannot find symbol symbol : class Subscription location: class SubscriptionTest?
Subscription S = new Subscription(200,3) ;
^
4 errors

I have create the Subscription class(first program) but as you can see the test program didn't recognize it. Can you help me to run this application?

Comment by uprime812, Dec 25, 2008

Hello achourso,

As you compile SubscriptionTest?, the compiler needs Subscription.class but fails to find it. The easiest solution for now is to put both your java files, so Subscription.java and SubscriptionTest?.java in the SAME directory. The compiler will then find your class files without further help.

Of course it is also possible to put them in different directories.

Comment by p...@broadway-performance-systems.com, Dec 25, 2008

You'll want to correct this line:

assertTrue(S:pricePerMonth() == 1.0) // replace colon with period

Comment by uprime812, Dec 31, 2008

You're right. Thanks.

Comment by eshar...@mac.com, Jan 02, 2009

To get the commands in the "Compiling and executing your test" section to work, I changed the semicolons to colons. I seen another article that used colons.

http://www.ibm.com/developerworks/library/j-classpath-unix/

Are the semicolons typos?

I'm using Mac OS X.

Comment by lovema...@vip.qq.com, Jan 04, 2009

junit how to it?

Comment by christiaanhees, Jan 04, 2009

@eshar: it depends on your operating system if you should use ':' or ';' to separate entries in your classpath. The commands in this tutorial are for Windows users. For Linux (and Mac) users the commands should be:

prompt> javac -cp .:<full path to JUnit.jar> SubscriptionTest.java

prompt> java  -cp .:<full path to JUnit.jar> org.junit.runner.JUnitCore SubscriptionTest

That should also fix achourso's problem.

Comment by udo.kalu, Feb 07, 2009

good and straight to the point

Comment by larachpatino, May 19, 2009

Thank you for writing this quick tutorial, it was really helpful.

Comment by leekiangiap, Jul 02, 2009

Really a big Thanks for this tutorial !!

One thing , java convention again ... When I tweak around with this code to test out JUnit for period of times,

Subscription S = new Subscription(200,2) ; assertTrue(S.pricePerMonth() == 1.0) ;

the "S" naming with uppercase letter keep disturbing me to think that the pricePerMonth() is a static method

anyway , not big issue here

again , THANKS !!!

Comment by neenee401, Jul 10, 2009

I think I just need another pair of eyes on this but I am getting an error when I tried to compile SubscriptionTest?. I have everything in the same directory and I downloaded & opened the junit-4.6.jar file, but I keep getting the following errors: "<identifier> expected on the import static org.juinit.Assert.; line 2....

Comment by uprime812, Jul 11, 2009

My guess is that you are using java version 1.4; import static is a feature of >= 1.5

Comment by neenee401, Jul 11, 2009

I think it's something else because I typed "C:\>java -version", here at home not at work this time, and got the following results: java version "1.6.0_10" Java(TM) SE Runtime Environment (build 1.6.0_10-b33) Java HotSot?(TM) Client VM (build 11.0-b15, mixed mode, sharing)

by the way here is the entire error printout:

C:\Java Workbench\Projects\EmergencyProc?\SubscriptionTest?.java:1: package org.junit does not exist import org.junit. ; ^ C:\Java Workbench\Projects\EmergencyProc?\SubscriptionTest?.java:2: package org.junit does not exist import static org.junit.Assert. ;

^
C:\Java Workbench\Projects\EmergencyProc?\SubscriptionTest?.java:6: cannot access Test bad class file: .\Test.class class file contains wrong class: org.junit.Test Please remove or make sure it appears in the correct subdirectory of the classpath.
@Test
^

Tool completed with exit code 1

thanks

Comment by uprime812, Jul 14, 2009

It looks like different errors are now given. It seems like java has a problem in finding your junit ("...package org.junit does not exist..."). I will have to ask this stupid question :), but do you have junit 4.x included in your classpath?

Comment by neenee401, Jul 16, 2009

I typed "path" at my c prompt and got the following:

PATH=C:\oraclexe\app\oracle\product\10.2.0\server\bin;C:\WINNT\system32;C:\WINNT;C:\WINNT\system32\WBEM;C:\Program Files\Java\jdk1.5.0_05\jre\bin;C:\Program Files\Java\jdk1.5.0_05\bin;C:\Program Files\\Maestro Learning\Common;C:\Program Files\QuickTime?\QTSystem\;C:\adabas\bin;C:\adabas\pgm;C:\Program Files\Java\jdk1.6.0_10\bin

It's been awhile since I messed with it but I will obviously have to change it.

thanks

Comment by sebafontana78, Jul 23, 2009

Well, there's a bad lesson in here: money shouldn't be stored in a double! That's why someone needed to do some rounds to make it pass. Doubles are for aprox values, and you don't want the aprox amount of euros, you want the exact amount of cents, so store your money in int or long, as cents.

Regards !

Comment by uprime812, Aug 01, 2009

Well, in the example it is NOT stored in double, but in int. The class just have a function that express it in double.

Comment by SSPriyaa, Aug 27, 2009

Hey, how to generate reports on the list of test cases that failed /passed? Also, is there a way to run only the failed test cases again and merge the results?

Comment by eatest.ironfist, Sep 10, 2009

First uprime812, i want to thank you so much! Most Junit tutorials are aloof and a little intimidating. The few minutes i spent with yours has really helped me and boosted my confidence. Thank you.

Second, in the compiling and execution test section, i entered in my cmd prompt and received the following: C:\junit\junit4.7>javac -cp .;C:\junit\junit4.7 junit_tut1_tc.java javac: file not found: junit_tut1_tc.java Usage: javac <options> <source files> use -help for a list of possible options

Is their a specific place my file/class junit_tut1_tc.java has to be?

Thanks, MrBCut

Comment by joseph.acapulco, Oct 08, 2009

C:\junit\junit4.7>javac -cp .;C:\junit\junit4.7 junit_tut1_tc.java

don't forget the jar C:\junit\junit4.7>javac -cp .;C:\junit\junit4.7\junit<version>.jar junit_tut1_tc.java

Comment by billnelson5.billnelson5, Oct 15, 2009

Thanks for the quick and straight forward tutorial. Very nice jumpstart for an agile newbie.


Sign in to add a comment
Hosted by Google Code