Unit Testing - Colorado School of Mines

Download Report

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()); }