$Revision: 1267 $ $Date: 2013-03-26 14:18:06 +1100 (Tue, 26 Mar 2013) $
The purpose of this lab class is to get practical experience with testing to debug a given program. Increasing your SVN skills at the same time. The practical experience is in using JUnit testing framework to test a Java based application. We shall also use JUnit for black box testing using some classes from the standard Java API.
Get practical skills in writing JUnit test classes, put them into a JUnit testing suite, and run it.
Use a class specification to construct test data, and use JUnit framework to actually test standard Java API classes.
Review your notes from
Download the JUnit 4.5 java archive (i.e. a .jar) file and place it in your home bin directory (in $HOME/bin). Set the CLASSPATH environment variable:
(for csh or tcsh)
if ( ~ $?CLASSPATH ) setenv CLASSPATH ; endif setenv CLASSPATH $CLASSPATH.:$HOME/bin/junit-4.5.jar
(if your shell is bash use
export CLASSPATH="$CLASSPATH:.:$HOME/bin/junit-4.5.jar"
to get the same result).
work in a new directory called lab03
inside your existing lab1 directory. lab1
should be under SVN version control (from your work in lab1),
Update it (svn update) - or check it out
svn co.
Download the
code which was used to illustrate the JUnit testing practices in
the lectures, all updated for JUnit 4.x.
Unpack it:
jar -xvf lab03-junit4.jar
compile all of the Java source code files:
javac *.java
and run each of the tests: for example
junit.textui.TestRunner TestTriangle
The test classes have a name of the form Test.... Look at
the source code of the Test classes, and compare with the
corresponding class (for TestTriangle, compare with the
class Test).
Create a new eclipse project for lab03.
Choose
Create new
project from existing source
to use these un-jarred files as this project source (why this way,
rather than create project from jar? it is so we can do some version
control, in a minute).
Add
the junit4
library to the project properties
(Project->Properties->JavaBuildPath->Libraries->AddLibrarycode>).
Select JUnit4.
You
can then select one of the test source files, such as TestTriangle
or the test suite AllTests,
and RunAs
a JUnit test.
Eclipse will create an internal window for junit. and should show the lovely green bar
If
this does not work (e.g. JUnit reports"no test cases found")
you may have to (a) force recompilation with File->Refresh
or (b) change your files/directories/packages structure: ask your
tutor.
The
test classes have a name of the form Test....
Look at the source code of the Test classes, and compare with the
corresponding class (for TestTriangle,
compare with the class Test).
revised Tue 7/4/09
You now have to bring the lab03 source files under Subversion control. We do this outside of SVN's automatic tools so that we can see what is happening (we are learning about SVN, not just using it as automagic).
First, remove the .class files (you do not need them in
the repository) – [eclipsers: quit eclipse first! so it does not
regenerate these .class files while you are not looking]
execute
rm *.class
Then step up one level (assuming you were in the directory ~/comp2100/labs/lab1/lab03/ to which you downloaded the files), execute
cd ..
and you will be in directory ~/comp2100/labs/lab1.
Now
add your newly made lab03 directory to SVN version
control:
svn add lab03
and commit the change to the repository ("add" merely informs the local working copy that you wish to add the new directory)
svn commit -m'lab 3 junit original'
Change back down into this (new) working copy directory
cd lab03
That is the normal way to add a new directory and its files into your
version controlled repository.
Now for the extra feature: to make
svn annotate the source files with the date, the ID of the updating
author (that is now you) and the revision number, execute the
following command:
svn propset svn:keywords "Date Author Revision" *.javapropset means "set a property": the name of the property is "svn:keywords" and its value is that string.
SVN does something special when a file has properties like this.
Every time you commit and update, the source of the file is changed by SVN: the special string $Author: cwj $ and $Revision: 850 $ in the source file (in this case they are in comments in the class headers) will now expand to the right values of modification date, author, and revision number every time you check the files out.
Having automatic file tags like this is very good practice because it makes a note of the version and a bit of recent history in each copy of the source file, and helps you recover from the problem of too many versions and working copies scattered around.
That is, instead of the original generic comment
* @author $Author: cwj $ * @version $Revision: 1267 $ * $Date: 2013-03-26 14:18:06 +1100 (Tue, 26 Mar 2013) $
you will get a "personalised" comment like
* @author $Author: cwj $ * @version $Revision: 1267 $ * $Date: 2013-03-26 14:18:06 +1100 (Tue, 26 Mar 2013) $
We use this in many of our programs and webpages for this course.
Look inside the Triangle.java source file. It contains several new methods which were not mentioned in lectures. The test code does not yet check the correctness of the new methods. Read the comments inside the source code, understand what the new methods do, and work out different inputs and expected outputs for each method.
This is the important step of designing test cases.
Use your newly designed test cases, and write new test methods in the TestTriangle.java file. According the JUnit prescription, you have to employ assertions from the Assert class.
You don't have to fully understand the implementation of methods in the Triangle class. It's enough to understand the "contract" of the methods, and to construct the expected output (the return value) for a given input (the actual parameter values taken by the method). For example, one of the new methods in the Triangle class is isInside(Point p) which returns a boolean value depending on whether the Point p lies inside the this triangle, or outside. The implementation of this method involves although elementary, but quite complex algorithm (and I am not quite sure that I've got everything right). Your task would be to test this implementation by applying several "obvious" test cases when you can be certain about the outcome. Start with a simple case and gradually make them more general. Use simple drawing to help yourself. For instance, create a triangle
Triangle t = new Triangle(new Point(0,0), new Point(1,0), new Point(0,1));
It's quite easy to see that any point with the positive coordinates (x,y) which satisfy the inequality
x + y <= 1
lies inside the triangle t. Also, as you can easily see, if either of the (x,y)-coordinates of Point p is greater (or smaller) than the corresponding coordinate of any of the triangle's three vertices, we can be sure that the point p lies outside of the triangle.
Use a calculator (on a mobile phone or in the Linux box in the front of you), if necessary to construct more sophisticated tests. Think of the boundary values for the test. How to handle a situation if the point lies exactly on the triangle border? What if the triangle is degenerate?
Other classes (Point, PlanarVector, Complex) also have not been fully tested. Write new methods in the corresponding test classes.
Of course, your tests cannot be exhaustive (and should not aim to be). Write test methods, compile the test class, call the TestRunner (or RunAs JunitTest in eclipse) and see your tests in the Swing GUI window.
When you have explored the program with a few tests, work
with your neighbours as a small ad hoc collaborative team:
design a testing strategy. The aim is to discover what is
wrong with this code, as quickly as possible, using a small
number of tests. The team is there to discuss strategy and use
the smartness of the group. It'll be good if you can find an error in
the "production" code using the JUnit tests.
Be ready
to report the test failures and what was your strategy, to the tutor
and to the class.
These exercises illustrate some more powerful extensions in
technique and in theory to setting up test cases and applying
JUnit.
Choose one or more that interest you for practical work.
These exercises can also be used as study exercises on the design of
test cases.
From working with TestTriangle.java, you can agree that creating the test data can be time and intellect consuming. Often these data are obtained from real experiments. Or, using different algorithms (which are known to be correct, but you are trying to implement a new one and use the old for testing). One way or another, these test data represent valuable assets. They can be stored somewhere in a separate file, or even in a secure database.
Write a file which contains as many lines as you have test value pairs (input and expected output). Write a test method which can be used on such test data files. Namely, inside the test class write a setUp() method which will open a stream from the test file, will read a line, parse it by selecting the input value and expected output value, store these values in the local object item of TestItem class (which can hold a pair of data accessible by getInputValue() and getExpectedOutput() methods), and put this TestItem instance into an ArrayList (or another suitable class which implements the Collection interface) object testData (by testData.add(item)). When all the test data from the test file are read, the setUp() method will end. The testData will then be used in a test method, which will contain a loop for iterating through the Collection object testData. For every element (item) in the testData, the input and expected output values will be extracted and used in the actual test involving the call for an appropriate assert method. The tearDown() method can be used to close the stream from the test data file, and reset the variables.
When the type of the test data and the test file format change, only partial recoding in the setUp() method will be required. Using Java 1.5 generic features can keep the amount of re-coding relatively small.
The JUnit approach was originally conceived for white box
testing: the author of the tests can see the source code of methods
of the class. However, it can be also used for black (grey) box
testing, when the production code is not available, but the unit
(class) contract and specification allows to formulate test cases.
For example: in this exercise, you will take one of the standard
classes from, eg, java.util, or java.util.regex
packages, and test it using the JUnit framework. I suggest you choose
either the java.util.Scanner class, or
java.util.regex.Pattern class, and write the test classes
TestScanner, or TestPattern, correspondingly.
For example, look up the java.util.Scanner API, and see
what methods this class has to offer.
One of the methods is
next(String pattern) which returns the first substring
matching the specified string pattern, and advances past the
input that matched the pattern. Write several strings with simple
patterns to test the described behaviour. (Boundary cases?
Equivalence cases? What test design principle applies?)
A similar method next(Pattern pattern) allows you to handle more general case of a regular expression given by the Pattern instance pattern. Extend your tests to this method.
Design and implement an
efficient JUnit test set for the gaga colour utility class methods in
the assignment distribution, to make an improvement on the few tests
that are included in the class main
method. You may like to provide tests for the cases that gag does not
yet handle: contributing these tests (in your assignment submission)
would be a contribution to the ongoing development of gaga.