|
JUnitQuickTutorial
A quick JUnit tutorial for beginners
JUnit 4.x Quick Tutorialby 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 firstIf 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 exampleLet 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 testLet us write two simple tests to check if pricePerMonth correctly calculates the price per month:
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 testNext 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: 2The 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. |
Sign in to add a comment
Why do you prefix your test methods with "test"? What if you used something more expressive?
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"?
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.
You're right! Thanks!
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
Java names like test_if_pricePerMonth_returns_Euro() gives me Corba nightmares.
Discover Java naming conventions before publishing next time.
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.
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()
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.
I think your underscores are fine when describing an action. It gives it a little scheme feel which is comfortable.
Beautiful and straight-forward. Thanks! Passes for
public double pricePerMonth()
Sweet!
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?
SubscriptionTest?.java:12: cannot find symbol symbol : class Subscription location: class SubscriptionTest? SubscriptionTest?.java:19: cannot find symbol symbol : class Subscription location: class SubscriptionTest? SubscriptionTest?.java:19: cannot find symbol symbol : class Subscription location: class SubscriptionTest? 4 errorsI 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?
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.
You'll want to correct this line:
assertTrue(S:pricePerMonth() == 1.0) // replace colon with period
You're right. Thanks.
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.
junit how to it?
@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:
That should also fix achourso's problem.
good and straight to the point
Thank you for writing this quick tutorial, it was really helpful.
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 !!!
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....
My guess is that you are using java version 1.4; import static is a feature of >= 1.5
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.Tool completed with exit code 1
thanks
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?
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
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 !
Well, in the example it is NOT stored in double, but in int. The class just have a function that express it in double.
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?
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
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
Thanks for the quick and straight forward tutorial. Very nice jumpstart for an agile newbie.