Transcript Unit Testing - Colorado School of Mines
Unit Testing
Topics
• Motivation • JUnit framework • Basic JUnit tests – static methods • Ensure exceptions thrown • @Before and @BeforeClass – instance methods • Test floating point values
}
JUnit Motivation
• Goal: relieve human from task of comparing expected and actual values
public class Utilities { public static int calcPerimeter(int length, int width) { return 2 * (length + width); } public static void main(String [] args) { // Ensure output is 16 System.out.println(Utilities.calcPerimeter(2,6)); }
Test-Driven Development (TDD)
1. Add a test (or tests) for an new unit of functionality (unit === method) 2. Run prior tests, see new test fail – may write
stubs
so code compiles 3. Implement new functionality 4. Run tests, see them succeed 5. Refactor – clean code rocks!
6. Repeat
Tests First
• Some “easy” examples – How would you test a function to calculate the area of a triangle?
– How would you test a function to calculate the circumference of a circle?
– How would you test a function that calculates wind chill?
JUnit Framework
• Test Case . Method that runs one or more tests. • Test Suite . Collection of tests run at the same time. Includes one or more test cases/methods.
• Test Runner . Utility used to run a test suite. – In Eclipse, shows a graphical presentation. – May also be a command-line version (Ruby).
• JUnit framework available from http://junit.org
Packages in Java
• A package is a way to bundle classes by functionality
Customers don’t really want to see all your test code!
Create a Package
In Eclipse: • Create a Java project, click on src • Select new package icon • Enter an appropriate name • For this example, create a package named windchill * • Select the package, create a new class named MyTempConverter
* for distribution, you’d want to ensure unique http://www.efsavage.com/blog/posts/java_package_conventions/
More on Packages
• The package statement must be the first line in the file, e.g.:
package windchill; public class MyTempConverter { … }
• Each package has its own directory (in the file structure)
TDD Example: Windchill
See the formula and chart: http://www.nws.noaa.gov/om/windchill/index.shtml
Windchill formula: • 35.74 + 0.6215T - 35.75V (**0.16) + 0.4275TV(**0.16)
The TempConverter class
•
First we create the “failing” methods – so that our tests will at least compile package windchill; public class MyTempConverter { public static { return -1; } long windChill(int speed, int temperature) }
Why does it make sense for this method to be static?
Adding the tests
• Create a package for your tests • Add your test classes to the package – Select the test package – Create a new class of type JUnit Test Case
Eclipse and JUnit
1. Select JUnit Test Case 2. Select JUnit Test Case We’ll use JUnit 4 Pick a meaningful name click
Eclipse and JUnit continued
Dialog for class to test: You must type in package Then you’ll see possible classes
Eclipse prompts for library
Build Path
If Eclipse creates a JUnit test for you, you’ll be prompted to add library.
To add a library later: Right-click on project name (in package explorer) Select Build Path Select Add Libraries Select JUnit 4
JUnit v4
• New Java Feature: annotations • Places a “marker” in code that is interpreted by another tool • For JUnit, marker/annotation is @Test • No need to extend TestCase (JUnit3) • Must import org.junit.*;
Some windchill tests
package tests; import junit.framework.Assert; import static org.junit.Assert.*; import Windchill.MyTempConverter; public class TestWindchillCalcs { @Test public void testWindchill() { long expected = -11; long actual = MyTempConverter.windChill(5, 0);
assertEquals(expected, actual); // local variables not needed
assertEquals(3, MyTempConverter.windChill(10, 15));} }
The TempConverter class
•
Now add code so that the tests pass package Windchill; public class MyTempConverter { } } public static long windChill(int speed, int temperature) { double newTemp = 35.74 + 0.6215*temperature 35.75 * Math.pow(speed, 0.16) + 0.4275 * temperature
* Math.pow(speed,0.16);
return Math.round(newTemp); } public static void main(String[] args) { System.out.println(MyTempConverter.windChill(10, 30));
Run as JUnit Test
JUnit Execution
All is well! Error! Look at failure trace for explanation
Can also test for expected exceptions
package Windchill; public class BadInputException extends RuntimeException { public BadInputException() {} public BadInputException(String msg){ super(msg); } } @Test (expected = BadInputException.class) public void testWindchillLowSpeed() throws BadInputException { long actual = MyTempConverter.windChill(4, 10); }
Exception class JUnit test
public static long windChill(int speed, int temperature) { if (speed < 5) } throw new BadInputException("Windchill not valid if speed < 5"); double newTemp = 35.74 + 0.6215*temperature 35.75 * Math.pow(speed, 0.16) + 0.4275 * temperature * Math.pow(speed,0.16); return Math.round(newTemp);
Discuss with a partner
• When would we need the throws clause on the test?
• What happens if the (expected) phrase is removed?
Testing instance methods
• When testing a class that has instance methods (most often), the test will need to create an instance • @BeforeClass static.
and @AfterClass annotated methods will be run exactly once during your test run - at the very beginning and end of the test as a whole, before anything else is run. In fact, they're run before the test class is even constructed, which is why they must be declared • The @Before and @After methods will be run before and after every test case, so will probably be run multiple times during a test run
Example to illlustrate @Before
package game; public class Location { private int x, y; public void move(int dx, int dy) { x += dx; y += dy; } // Also has constructors and getters }
Test it – see @Before message
public class TestLocation { private Location location; @Before public void setUp(){
System.out.println("In @Before");
location = new Location(); } @Test public void testMove() { location.move(5, 10);
assertEquals(5, location.getX()); assertEquals(10, location.getY());
} @Test public void testMove2() { location.move(5, 10);
assertEquals(5, location.getX()); assertEquals(10, location.getY());
location.move(5, 10);
assertEquals(10, location.getX()); assertEquals(20, location.getY());
} }
Example of @BeforeClass
• Assume the board set up is complex, and you don’t need to reset between tests. package game; public class Board { private String gameStatus; public void init() { gameStatus = "Long game set up is done!"; } public String getGameStatus() { return gameStatus; } }
The @BeforeClass test
import game.Board; public class TestBoard { static Board
board;
@BeforeClass not run for each test instance – just the entire test suite – so variables must be
static
@BeforeClass public static void setUpBeforeClass() throws Exception { System.
out.println("In @BeforeClass"); board = new Board(); board.init();
} @Test public void test1() {
assertEquals(board.getGameStatus(), "Long game set up is done!");
} } @Test public void test2() {
assertEquals(board.getGameStatus(), "Long game set up is done!");
} This is an advanced detail – will not be covered on tests, BUT may be helpful in your projects
Testing floating points
• Remember that floating point values should not be compared exactly.
public class MyConverter { public static double INCHES_TO_METERS = 0.0254;
}
public static double englishToMeters(int feet, int inches)
{
return 0;
//int totalInches = feet * 12 + inches; //return totalInches * INCHES_TO_METERS; }
• Use a tolerance
The test
public class TestConversions { public static double
EPSILON = .0001;
} @Test public void testFeetToMeters() { double expected = 0.0508; double actual = MyConverter.
englishToMeters(0, 2); assertEquals(expected, actual, EPSILON );
expected = 0.3556; actual = MyConverter.
englishToMeters(1, 2); assertEquals(expected, actual, EPSILON );
What tests could you write for…
• A chess game • A linked list library • String library • UPC code
Advanced Topic
• The static import construct allows unqualified access to static members
import static org.junit.Assert.assertEquals;
@Test public void testMove3() { location.move(5, 10); assertEquals(5, location.getX()); assertEquals(10, location.getY()); }