300 likes | 502 Views
Unit Testing. Topics. Motivation JUnit framework Basic JUnit tests – static methods @Before and @BeforeClass – instance methods Ensure exceptions thrown Test floating point values. Test-Driven Development (TDD). Add a test (or tests) for an new unit of functionality (unit === method)
E N D
Topics • Motivation • JUnit framework • Basic JUnit tests – static methods • @Before and @BeforeClass – instance methods • Ensure exceptions thrown • Test floating point values
Test-Driven Development (TDD) • Add a test (or tests) for an new unit of functionality (unit === method) • Run prior tests, see new test fail • may write stubs so code compiles • Implement new functionality • Run tests, see them succeed • Refactor – clean code rocks! • Repeat
JUnit Motivation • Goal: relieve human from task of comparing expected and actual values public class Utilities { public static intcalcPerimeter(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)); } }
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 long windChill(int speed, int temperature) • { • return -1 • } • } 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 2. Select JUnit Test Case 1. Select JUnit Test Case Pick a meaningful name We’ll use JUnit 4 click
Eclipse and JUnit continued Dialog for class to test: You must type in package Then you’ll see possible classes
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 org.junit.*; import Windchill.MyTempConverter; public class TestWindchillCalcs { @Test public void testWindchill() { long expected = -11; long actual = MyTempConverter.windChill(5, 0); Assert.assertEquals(expected, actual); expected = 3; actual = MyTempConverter.windChill(10, 15); Assert.assertEquals(expected, actual); } }
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)); • } • }
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); } } Exception class @Test (expected = BadInputException.class) public void testWindchillLowSpeed() throws BadInputException { long actual = MyTempConverter.windChill(4, 10); } 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); }
Testing instance methods • When testing a class that has instance methods (most often), the test will need to create an instance • @BeforeClass 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 static. • 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); Assert.assertEquals(5, location.getX()); Assert.assertEquals(10, location.getY()); } @Test public void testMove2() { location.move(5, 10); Assert.assertEquals(5, location.getX()); Assert.assertEquals(10, location.getY()); location.move(5, 10); Assert.assertEquals(10, location.getX()); Assert.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 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!"); } } } @BeforeClass not run for each test instance – just the entire test suite – so variables must be static 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; } }
The test • Use a tolerance public class TestConversions { public static double EPSILON = .0001; @Test public void testFeetToMeters() { double expected = 0.0508; double actual = MyConverter.englishToMeters(0, 2); Assert.assertEquals(expected, actual, EPSILON); expected = 0.3556; actual = MyConverter.englishToMeters(1, 2); Assert.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 • So when should you use static import? Very sparingly! import static org.junit.Assert.assertEquals; @Test public void testMove3() { location.move(5, 10); assertEquals(5, location.getX()); assertEquals(10, location.getY()); }