Functional testing
Integration testing
Exploratory testing
Acceptance testing
System testing
Unit testing
Glass box: testing the nonpublic API
White box: testing the public (and protected) API
Grey box: testing internal components
Black box: testing the external interface; e.g. the GUI
Before
After
During
First
Last
Continuous
Faster development; faster time to market
More robust, error free code
YAGNI: Writing tests first avoids writing unneeded code.
Easier to find and fix bugs
Easier to add features or change behavior; less worry about unintentionally introducing bugs
Makes refactoring/optimization possible: any change that doesn't break a test suite is de facto acceptable.
Often a developer will want to make some optimizations to a piece of code on which a lot of user-interface behavior depends. Sure, the new table sorter will be ten times faster and more memory efficient, but are you sure the changes won't affect the report generator? Run the tests and find out. A developer is much more likely to run a test that has encapsulated how to set up and test a report than he is to launch the application by hand and try to track down who knows how to do that particular test.
Run automatically without any human participation beyond launching the test suite (and maybe not even that)
Results should be pass/fail
Test failures need to be blindingly obvious
Test must be reproducible and consistent
Write a test for a feature
Code the simplest thing that can possibly work
Run the test
Test passed ? goto 1 : goto 2
Have a new feature? Write a test.
Want to change the API? Write a test.
Find a bug? Write a test.
Entire test suite should run before each checkin.
All tests are pass-fail
No new features allowed until all tests are passing
Continuous integration
One-button build process
Failure to adhere to spec? Write a test.
Find a bug? Write a test.
Unspecified behavior? Write a test.
Process issue: you need commit privileges
de facto standard unit testing framework
Bundled in most IDEs
http://www.junit.org/
Designed for whitebox unit testing but the framework works pretty well for other forms:
Glassbox, greybox, blackbox
Integration
User Acceptance
etc.
Useful to have one complete test suite the programmers can run with a pass fail result.
Represents a rational number such as 2/3 or 17/5 or 5
From org.apache.commons.lang.math
You'll find it on the CD in the lib/commons-lang-2.1 folder
Test methods have names like testFoo
Test results are verified by assertEquals()
Test classes extend junit.framework.TestCase
A simple test for addition:
import junit.framework.TestCase;
import org.apache.commons.lang.math.*;
public class FractionTest extends TestCase {
public void testAddition() {
Fraction half = Fraction.ONE_HALF;
Fraction fourth = Fraction.ONE_QUARTER;
Fraction actual = half.add(fourth);
Fraction expected = Fraction.getFraction(3, 4);
assertEquals(expected, actual);
}
}
For simplicity put junit.jar and commons-lang-2.1.jar from the lib directory into a new empty directory.
Put this code into a file named FractionTest.java in that same directory:
import junit.framework.TestCase;
import org.apache.commons.lang.math.*;
public class FractionTest extends TestCase {
public void testAddition() {
Fraction half = Fraction.getFraction(2, 4);
Fraction fourth = Fraction.getFraction(1, 4);
Fraction actual = half.add(fourth);
Fraction expected = Fraction.getFraction(3, 4);
assertEquals(expected, actual);
}
}
Compiling:
$ javac -classpath junit.jar:commons-lang-2.1.jar FractionTest.java
Running:
$ java -classpath .:junit.jar:commons-lang-2.1.jar junit.swingui.TestRunner FractionTest
Add two tests of your choice
Each test should be a separate test method.
There should now be three tests.
Can anyone find a bug? It took me eight tests to find my first bug in this class.
Unit testing requires that each test be completely independent of all other tests.
Not a requirement of all types of tests (functional, integration, user acceptance, etc.) but JUnit is primarily a unit testing framework.
No state shared between methods
All instance data reinitialized before each test run.
Each test is run with a clean, new object.
Test adding two elements to a list.
private LinkedList list = new LinkedList();
public void testAdd() {
String s = "STARWest 2005";
list.add(s);
Object o = list.getFirst();
assertEquals(s, o);
}
public void testSizeAfterAddingOneElementIsOne() {
String s = "STARWest 2005";
list.add(s);
assertEquals(1, list.size());
}
Both tests pass!
Order in which tests are run doesn't matter
Expensive initialization slows down tests.
One of these tests will fail:
private static LinkedList list = new LinkedList();
public void testAdd() {
String s = "STARWest 2005";
list.add(s);
Object o = list.getFirst();
assertEquals(s, o);
}
public void testSizeAfterAddingOneElementIsOne() {
String s = "STARWest 2005";
list.add(s);
assertEquals(1, list.size());
}
Sometimes the setup can be more complex than you want to put in a constructor or a field initializer
Sometimes you want to reinitialize static data
Sometimes you just want to share setup code between different methods.
setUp
method is run before each test:
protected void setUp() {
list = new LinkedList();
super.setUp();
}
setUp()
should call super.setUp()
Move the shared code into fields.
Write a setUp()
method that initializes these fields.
import junit.framework.TestCase;
import org.apache.commons.lang.math.*;
public class FractionTest extends TestCase {
private Fraction half;
private Fraction fourth;
protected void setUp() {
half = Fraction.getFraction(2, 4);
fourth = Fraction.getFraction(1, 4);
}
public void testAddition() {
Fraction actual = half.add(fourth);
Fraction expected = Fraction.getFraction(3, 4);
assertEquals(expected, actual);
}
public void testSubtraction() {
Fraction actual = half.subtract(fourth);
Fraction expected = fourth;
assertEquals(expected, actual);
}
public void testAddNumberToItself() {
Fraction actual = half.add(half);
Fraction expected = Fraction.ONE;
assertEquals(expected, actual);
}
}
tearDown
method:
protected void tearDown() {
half = null;
fourth = null;
System.gc();
}
Rarely necessary
A no-args constructor that calls the superclass no-args constructor
A one-arg constructor that takes a String
name and calls the superclass constructor that takes a String
name
Add a constructor to your test class.
public FractionTest() {}
public FractionTest(String name) {
super(name);
}
First argument to each assertFoo
method can be a string that's printed if the assertion fails:
public void testSubtraction() {
Fraction actual = half.subtract(fourth);
Fraction expected = fourth;
assertEquals("Could not subtract one fourth from two fourths",
expected, actual);
}
Add assertion messages to each test.
import junit.framework.TestCase;
import org.apache.commons.lang.math.*;
public class FractionTest extends TestCase {
private Fraction half;
private Fraction fourth;
protected void setUp() {
half = Fraction.getFraction(2, 4);
fourth = Fraction.getFraction(1, 4);
}
public void testAddition() {
Fraction actual = half.add(fourth);
Fraction expected = Fraction.getFraction(3, 4);
assertEquals("Could not add 1/2 to 1/4", expected, actual);
}
public void testSubtraction() {
Fraction actual = half.subtract(fourth);
Fraction expected = fourth;
assertEquals("Could not subtract 1/4 from 1/2",expected, actual);
}
public void testAddNumberToItself() {
Fraction actual = half.add(half);
Fraction expected = Fraction.ONE;
assertEquals("Could not add 1/2 to itself using same object",
expected, actual);
}
}
As always with floating point arithmetic, you want to avoid direct equality comparisons.
public static void assertEquals(double expected, double actual, double tolerance)
public static void assertEquals(float expected, float actual, float tolerance)
public static void assertEquals(String message, double expected, double actual, double tolerance)
public static void assertEquals(String message, float expected, float actual, float tolerance)
Write a test for the double value of 5/3.
That is, write a test for the doubleValue()
method.
public void testDoubleValue() {
Fraction a = Fraction.getFraction(5, 3);
assertEquals(5.0/3.0, a.doubleValue(), 0.000000001);
}
Straight-forward because integer comparisons are straight-forward:
public static void assertEquals(boolean expected, boolean actual)
public static void assertEquals(byte expected, byte actual)
public static void assertEquals(char expected, char actual)
public static void assertEquals(short expected, short actual)
public static void assertEquals(int expected, int actual)
public static void assertEquals(long expected, long actual)
public static void assertEquals(String message, boolean expected, boolean actual)
public static void assertEquals(String message, byte expected, byte actual)
public static void assertEquals(String message, char expected, char actual)
public static void assertEquals(String message, short expected, short actual)
public static void assertEquals(String message, int expected, int actual)
public static void assertEquals(String message, long expected, long actual)
Write a test for the numerator and proper numerators of 5/3
That is, write tests for getNumerator()
and getProperNumerator()
The "proper numerator" is the 2 in the 1 and 2/3 form of 5/3. The proper numerator is always positive. (Is zero a boundary condition?)
public void testgetNumerator() {
Fraction a = Fraction.getFraction(5, 3);
assertEquals(5, a.getNumerator());
}
public void testgetProperNumerator() {
Fraction a = Fraction.getFraction(5, 3);
assertEquals(2, a.getProperNumerator());
}
How many edge conditions can we diagnose for getProperNumerator
alone?
Each of these could be a separate unit test, but that's tedious.
Store input and expected output in matching arrays and write one test to iterate through them.
Even better: store the input and output in a text or XML file.
Working in teams of 4, develop a list of different fraction pairs likely to expose bugs and cover the problem space. Then write a parameterized test that loads this data to test addition, multiplication, subtraction, and division.
This really isn't a unit test any more, but it's still useful.
Good assertion messages are critical to identify the actual failing test
But these can slow down the tests
One failing test masks subsequent test failures.
If any bugs show up, those test cases can be pulled out into explicit individual tests for debugging.
Advanced trick: generate source code for the tests from the input data
Sometimes the integration tests find bugs the unit testing can miss. In particular, unitarity can mask reinitialization failures:
This variant now runs all tests even if one test fails:
public void testIntegrationGetProperNumerator() {
int failures = 0;
StringBuffer failureMessage = new StringBuffer();
for (int i = 0; i < data.length; i++) {
int[] row = data[i];
Fraction f = Fraction.getFraction(row[0], row[1]);
try {
assertEquals(row[2], f.getProperNumerator());
}
catch (AssertionFailedError err) {
failures++;
failureMessage.append(err.getMessage() + "\n");
}
}
if (failures > 0) fail(failureMessage.toString());
}
assertEquals
uses the equals()
method:
public void testAEqualsA() {
Fraction a = Fraction.getFraction(17, 56);
assertEquals(a, a);
}
public void testAEqualsA() {
Fraction a = Fraction.getFraction(17, 56);
assertEquals(a, a);
}
public void testAEqualsA2() {
Fraction a = Fraction.getFraction(17, 56);
Fraction b = Fraction.getFraction(17, 56);
assertEquals(a, b);
}
assertSame
and assertNotSame
use the ==
operator:
public void testAIsA() {
Fraction a = Fraction.getFraction(17, 56);
assertSame(a, a);
}
public void testAIsNotA2() {
Fraction a = Fraction.getFraction(17, 56);
Fraction b = Fraction.getFraction(17, 56);
assertNotSame(a, b);
}
Does the last test go too far? is that really part of the class's contract?
For general boolean tests, one can assert that some condition is true
public static void assertTrue(boolean condition)
public static void assertTrue(String message, boolean condition)
Test superclass:
public void testSuperclass() {
assertTrue(fourth instanceof Number);
}
Can also assert that some condition is false
public static void assertFalse(boolean condition)
public static void assertFalse(String message, boolean condition)
public static void assertNull(Object o)
public static void assertNull(String message, Object o)
e.g. testing for memory leak:
public void testMemoryLeak() throws InterruptedException {
Fraction a = Fraction.getFraction(1, 2);
WeakReference ref = new WeakReference(a);
a = null;
System.gc();
System.gc();
System.gc();
Thread.sleep(1000);
assertNull(ref.get());
}
public static void assertNotNull(Object o)
public static void assertNotNull(String message, Object o)
public void testToString() {
assertNotNull(list.toString());
}
public static void fail()
public static void fail(String message)
Runtime exceptions cause tests to fail
Different than a test failure
But do not shutdown the VM or stop the other tests
Unexpected checked exceptions must be declared to be thrown from the test method
Otherwise treated the same as runtime exceptions
Errors are treated the same as a runtime exception
try
fail()
catch
public void testCantRetrieveFromEmptyList() {
try {
list.getFirst();
fail("Got Something from an empty list");
}
catch (NoSuchElementException success) {
}
}
Without looking at the JavaDoc,
can you add null to a Fraction
? What happens if you do?
i.e.
Is an exception thrown?
If so which one?
Or is null treated as zero?
Or is null returned?
Something else?
Write a test that determines the answer to this question, and then adjust it to match the behavior.
Is this behavior properly documented? If not, it's a bug.
public void testAddNull() {
Fraction a = Fraction.getFraction(5, 3);
try {
Fraction f = null;
a.add(null);
fail("added null");
}
catch (IllegalArgumentException success) {
// This really is a bug. This should be a NullPointerException
assertNotNull(success.getMessage());
}
}
Test data can be stored alongside with the test cases using relative paths
Sometimes the files can be created on the fly in a temporary directory
Write sample data onto a ByteArrayOutputStream
.
Convert to byte array
Read from a ByteArrayInputStream
Write onto a ByteArrayOutputStream
Test directly or convert to string.
Fraction implements Serializable
.
Write a test that serializes and deserializes a Fraction
object; That is:
Create a Fraction
Create a ByteArrayOutputStream
Chain an ObjectOutputStream
to the ByteArrayOutputStream
Write the Fraction object onto the ObjectOutputStream
using writeObject()
Close the ObjectOutputStream
Get a byte array from the ByteArrayOutputStream
using toByteArray()
Make a ByteArrayInputStream
from the byte array
Chain an ObjectInputStream
to the ByteArrayInputStream
Read the object from the ObjectInputStream
using readObject()
Verify that the object is equal to but not the same as the original object
public void testSerialization() throws IOException, ClassNotFoundException {
Fraction source = Fraction.getFraction(1, 2);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oout = new ObjectOutputStream(out);
oout.writeObject(source);
oout.close();
byte[] data = out.toByteArray();
ByteArrayInputStream in = new ByteArrayInputStream(data);
ObjectInputStream oin = new ObjectInputStream(in);
Fraction result = (Fraction) oin.readObject();
assertEquals(source, result);
assertNotSame(source, result);
}
Generally more than one test class per project
Often one (or more ) test classes per tested class
Multiple test classes can be combined in a test suite class:
public class FunctionTests {
public static Test suite() {
TestSuite result = new TestSuite();
result.addTest(new TestSuite(TranslateFunctionTest.class));
result.addTest(new TestSuite(SubstringTest.class));
result.addTest(new TestSuite(SubstringBeforeTest.class));
result.addTest(new TestSuite(SubstringAfterTest.class));
result.addTest(new TestSuite(LangTest.class));
result.addTest(new TestSuite(LastTest.class));
result.addTest(new TestSuite(ConcatTest.class));
result.addTest(new TestSuite(ContainsTest.class));
result.addTest(new TestSuite(StringLengthTest.class));
result.addTest(new TestSuite(StartsWithTest.class));
result.addTest(new TestSuite(CountTest.class));
result.addTest(new TestSuite(LocalNameTest.class));
result.addTest(new TestSuite(NameTest.class));
result.addTest(new TestSuite(NamespaceURITest.class));
result.addTest(new TestSuite(SumTest.class));
result.addTest(new TestSuite(NumberTest.class));
result.addTest(new TestSuite(RoundTest.class));
result.addTest(new TestSuite(StringTest.class));
result.addTest(new TestSuite(BooleanTest.class));
result.addTest(new TestSuite(CeilingTest.class));
result.addTest(new TestSuite(FloorTest.class));
result.addTest(new TestSuite(IdTest.class));
result.addTest(new TestSuite(TrueTest.class));
result.addTest(new TestSuite(FalseTest.class));
result.addTest(new TestSuite(NotTest.class));
result.addTest(new TestSuite(NormalizeSpaceTest.class));
return result;
}
}
Multiple test methods can also be combined in a test suite class:
public class JaxenTests {
public static Test suite() {
TestSuite result = new TestSuite();
result.addTest(SAXPathTests.suite());
result.addTest(FunctionTests.suite());
result.addTest(CoreTests.suite());
result.addTest(DOMTests.suite());
result.addTest(JDOMTests.suite());
result.addTest(DOM4JTests.suite());
result.addTest(XOMTests.suite());
result.addTest(JavaBeanTests.suite());
result.addTest(PatternTests.suite());
result.addTest(BaseTests.suite());
result.addTest(HelpersTests.suite());
result.addTest(ExprTests.suite());
result.addTest(UtilTests.suite());
return result;
}
}
Public fields are part of the API and need to be tested too
Often these are constant.
Test that they're equal to the expected value:
public void testOneHalfField() {
Fraction a = Fraction.getFraction(1, 2);
assertEquals(a, Fraction.ONE_HALF);
}
Better to test through the public API
But can put test classes in same package as tested API
Use different src directories for separation
Test through the public API if at all possible
Delete unreachable private methods
The reflection API and setAccessible()
Template methods to test implementations
Especially important if you expect multiple independent implementations
Parameterized TestCase Pattern
Same test with much different data.
Pass the test data and the test method name into the test case constructor
Override runTest()
Instantiate multiple different instances of the test case in the suite method
import org.apache.commons.lang.math.Fraction;
import junit.framework.*;
import junit.textui.TestRunner;
public class DataDrivenTest extends TestCase{
private int numerator;
private int denominator;
private int result;
public DataDrivenTest(int numerator, int denominator, int result) {
super("testIntegrationGetProperNumerator");
this.numerator = numerator;
this.denominator = denominator;
this.result = result;
}
public static Test suite() {
TestSuite result = new TestSuite();
result.addTest(new DataDrivenTest(5, 3, 2));
result.addTest(new DataDrivenTest(-5, 3, 2 ));
result.addTest(new DataDrivenTest(0, 3, 0));
result.addTest(new DataDrivenTest(6, 2, 0));
return result;
}
public void runTest() {
testIntegrationGetProperNumerator();
}
public void testIntegrationGetProperNumerator() {
Fraction f = Fraction.getFraction(numerator, denominator);
assertEquals("Testing " +
numerator + "/" + denominator, result, f.getProperNumerator());
}
}
Test that what should happen, does happen
Test that what should not happen, doesn't
All public APIs
All protected APIs
Anything that is promised not to change
Private API
Package protected API?
Localized strings
Anything that is not promised
Every return value is at least one test.
Every argument is at least one test.
Every exception is at least one test.
Most sentences in the description are at least one test.
Starting from the top read the JavaDoc for the Fraction class.
What behavior is specified that we should test for?
What behavior is not specified that should be?
Is anything specified that we now know to be false?
Is anything specified correctly, but indicates incorrect behavior?
What did they do right?
What did they do wrong?
What did Apache forget to test?
/*
* Copyright 2002,2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang.math;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Test cases for the {@link Fraction} class
*
* @author Stephen Colebourne
* @author C. Scott Ananian
* @version $Id: FractionTest.java 155423 2005-02-26 13:08:30Z dirkv $
*/
public class FractionTest extends TestCase {
private static final int SKIP = 500; //53
public FractionTest(String name) {
super(name);
}
public static Test suite() {
TestSuite suite = new TestSuite(FractionTest.class);
suite.setName("Fraction Tests");
return suite;
}
public void setUp() {
}
//--------------------------------------------------------------------------
public void testConstants() {
assertEquals(0, Fraction.ZERO.getNumerator());
assertEquals(1, Fraction.ZERO.getDenominator());
assertEquals(1, Fraction.ONE.getNumerator());
assertEquals(1, Fraction.ONE.getDenominator());
assertEquals(1, Fraction.ONE_HALF.getNumerator());
assertEquals(2, Fraction.ONE_HALF.getDenominator());
assertEquals(1, Fraction.ONE_THIRD.getNumerator());
assertEquals(3, Fraction.ONE_THIRD.getDenominator());
assertEquals(2, Fraction.TWO_THIRDS.getNumerator());
assertEquals(3, Fraction.TWO_THIRDS.getDenominator());
assertEquals(1, Fraction.ONE_QUARTER.getNumerator());
assertEquals(4, Fraction.ONE_QUARTER.getDenominator());
assertEquals(2, Fraction.TWO_QUARTERS.getNumerator());
assertEquals(4, Fraction.TWO_QUARTERS.getDenominator());
assertEquals(3, Fraction.THREE_QUARTERS.getNumerator());
assertEquals(4, Fraction.THREE_QUARTERS.getDenominator());
assertEquals(1, Fraction.ONE_FIFTH.getNumerator());
assertEquals(5, Fraction.ONE_FIFTH.getDenominator());
assertEquals(2, Fraction.TWO_FIFTHS.getNumerator());
assertEquals(5, Fraction.TWO_FIFTHS.getDenominator());
assertEquals(3, Fraction.THREE_FIFTHS.getNumerator());
assertEquals(5, Fraction.THREE_FIFTHS.getDenominator());
assertEquals(4, Fraction.FOUR_FIFTHS.getNumerator());
assertEquals(5, Fraction.FOUR_FIFTHS.getDenominator());
}
public void testFactory_int_int() {
Fraction f = null;
// zero
f = Fraction.getFraction(0, 1);
assertEquals(0, f.getNumerator());
assertEquals(1, f.getDenominator());
f = Fraction.getFraction(0, 2);
assertEquals(0, f.getNumerator());
assertEquals(2, f.getDenominator());
// normal
f = Fraction.getFraction(1, 1);
assertEquals(1, f.getNumerator());
assertEquals(1, f.getDenominator());
f = Fraction.getFraction(2, 1);
assertEquals(2, f.getNumerator());
assertEquals(1, f.getDenominator());
f = Fraction.getFraction(23, 345);
assertEquals(23, f.getNumerator());
assertEquals(345, f.getDenominator());
// improper
f = Fraction.getFraction(22, 7);
assertEquals(22, f.getNumerator());
assertEquals(7, f.getDenominator());
// negatives
f = Fraction.getFraction(-6, 10);
assertEquals(-6, f.getNumerator());
assertEquals(10, f.getDenominator());
f = Fraction.getFraction(6, -10);
assertEquals(-6, f.getNumerator());
assertEquals(10, f.getDenominator());
f = Fraction.getFraction(-6, -10);
assertEquals(6, f.getNumerator());
assertEquals(10, f.getDenominator());
// zero denominator
try {
f = Fraction.getFraction(1, 0);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getFraction(2, 0);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getFraction(-3, 0);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
// very large: can't represent as unsimplified fraction, although
try {
f = Fraction.getFraction(4, Integer.MIN_VALUE);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getFraction(1, Integer.MIN_VALUE);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
}
public void testFactory_int_int_int() {
Fraction f = null;
// zero
f = Fraction.getFraction(0, 0, 2);
assertEquals(0, f.getNumerator());
assertEquals(2, f.getDenominator());
f = Fraction.getFraction(2, 0, 2);
assertEquals(4, f.getNumerator());
assertEquals(2, f.getDenominator());
f = Fraction.getFraction(0, 1, 2);
assertEquals(1, f.getNumerator());
assertEquals(2, f.getDenominator());
// normal
f = Fraction.getFraction(1, 1, 2);
assertEquals(3, f.getNumerator());
assertEquals(2, f.getDenominator());
// negatives
try {
f = Fraction.getFraction(1, -6, -10);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getFraction(1, -6, -10);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getFraction(1, -6, -10);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
// negative whole
f = Fraction.getFraction(-1, 6, 10);
assertEquals(-16, f.getNumerator());
assertEquals(10, f.getDenominator());
try {
f = Fraction.getFraction(-1, -6, 10);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getFraction(-1, 6, -10);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getFraction(-1, -6, -10);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
// zero denominator
try {
f = Fraction.getFraction(0, 1, 0);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getFraction(1, 2, 0);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getFraction(-1, -3, 0);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getFraction(Integer.MAX_VALUE, 1, 2);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getFraction(-Integer.MAX_VALUE, 1, 2);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
// very large
f = Fraction.getFraction(-1, 0, Integer.MAX_VALUE);
assertEquals(-Integer.MAX_VALUE, f.getNumerator());
assertEquals(Integer.MAX_VALUE, f.getDenominator());
try {
// negative denominators not allowed in this constructor.
f = Fraction.getFraction(0, 4, Integer.MIN_VALUE);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getFraction(1, 1, Integer.MAX_VALUE);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getFraction(-1, 2, Integer.MAX_VALUE);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
}
public void testReducedFactory_int_int() {
Fraction f = null;
// zero
f = Fraction.getReducedFraction(0, 1);
assertEquals(0, f.getNumerator());
assertEquals(1, f.getDenominator());
// normal
f = Fraction.getReducedFraction(1, 1);
assertEquals(1, f.getNumerator());
assertEquals(1, f.getDenominator());
f = Fraction.getReducedFraction(2, 1);
assertEquals(2, f.getNumerator());
assertEquals(1, f.getDenominator());
// improper
f = Fraction.getReducedFraction(22, 7);
assertEquals(22, f.getNumerator());
assertEquals(7, f.getDenominator());
// negatives
f = Fraction.getReducedFraction(-6, 10);
assertEquals(-3, f.getNumerator());
assertEquals(5, f.getDenominator());
f = Fraction.getReducedFraction(6, -10);
assertEquals(-3, f.getNumerator());
assertEquals(5, f.getDenominator());
f = Fraction.getReducedFraction(-6, -10);
assertEquals(3, f.getNumerator());
assertEquals(5, f.getDenominator());
// zero denominator
try {
f = Fraction.getReducedFraction(1, 0);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getReducedFraction(2, 0);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getReducedFraction(-3, 0);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
// reduced
f = Fraction.getReducedFraction(0, 2);
assertEquals(0, f.getNumerator());
assertEquals(1, f.getDenominator());
f = Fraction.getReducedFraction(2, 2);
assertEquals(1, f.getNumerator());
assertEquals(1, f.getDenominator());
f = Fraction.getReducedFraction(2, 4);
assertEquals(1, f.getNumerator());
assertEquals(2, f.getDenominator());
f = Fraction.getReducedFraction(15, 10);
assertEquals(3, f.getNumerator());
assertEquals(2, f.getDenominator());
f = Fraction.getReducedFraction(121, 22);
assertEquals(11, f.getNumerator());
assertEquals(2, f.getDenominator());
// Extreme values
// OK, can reduce before negating
f = Fraction.getReducedFraction(-2, Integer.MIN_VALUE);
assertEquals(1, f.getNumerator());
assertEquals(-(Integer.MIN_VALUE / 2), f.getDenominator());
// Can't reduce, negation will throw
try {
f = Fraction.getReducedFraction(-7, Integer.MIN_VALUE);
fail("Expecting ArithmeticException");
} catch (ArithmeticException ex) {}
}
public void testFactory_double() {
Fraction f = null;
try {
f = Fraction.getFraction(Double.NaN);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getFraction(Double.POSITIVE_INFINITY);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getFraction(Double.NEGATIVE_INFINITY);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = Fraction.getFraction((double) Integer.MAX_VALUE + 1);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
// zero
f = Fraction.getFraction(0.0d);
assertEquals(0, f.getNumerator());
assertEquals(1, f.getDenominator());
// one
f = Fraction.getFraction(1.0d);
assertEquals(1, f.getNumerator());
assertEquals(1, f.getDenominator());
// one half
f = Fraction.getFraction(0.5d);
assertEquals(1, f.getNumerator());
assertEquals(2, f.getDenominator());
// negative
f = Fraction.getFraction(-0.875d);
assertEquals(-7, f.getNumerator());
assertEquals(8, f.getDenominator());
// over 1
f = Fraction.getFraction(1.25d);
assertEquals(5, f.getNumerator());
assertEquals(4, f.getDenominator());
// two thirds
f = Fraction.getFraction(0.66666d);
assertEquals(2, f.getNumerator());
assertEquals(3, f.getDenominator());
// small
f = Fraction.getFraction(1.0d/10001d);
assertEquals(0, f.getNumerator());
assertEquals(1, f.getDenominator());
// normal
Fraction f2 = null;
int remainder, number1, number2 = 0;
for (int i = 1; i <= 100; i++) { // denominator
for (int j = 1; j <= i; j++) { // numerator
try {
f = Fraction.getFraction((double) j / (double) i);
} catch (ArithmeticException ex) {
System.err.println(j + " " + i);
throw ex;
}
f2 = Fraction.getReducedFraction(j, i);
assertEquals(f2.getNumerator(), f.getNumerator());
assertEquals(f2.getDenominator(), f.getDenominator());
}
}
// save time by skipping some tests! (
for (int i = 1001; i <= 10000; i+=SKIP) { // denominator
for (int j = 1; j <= i; j++) { // numerator
try {
f = Fraction.getFraction((double) j / (double) i);
} catch (ArithmeticException ex) {
System.err.println(j + " " + i);
throw ex;
}
f2 = Fraction.getReducedFraction(j, i);
assertEquals(f2.getNumerator(), f.getNumerator());
assertEquals(f2.getDenominator(), f.getDenominator());
}
}
}
public void testFactory_String() {
try {
Fraction.getFraction(null);
fail("expecting IllegalArgumentException");
} catch (IllegalArgumentException ex) {}
}
public void testFactory_String_double() {
Fraction f = null;
f = Fraction.getFraction("0.0");
assertEquals(0, f.getNumerator());
assertEquals(1, f.getDenominator());
f = Fraction.getFraction("0.2");
assertEquals(1, f.getNumerator());
assertEquals(5, f.getDenominator());
f = Fraction.getFraction("0.5");
assertEquals(1, f.getNumerator());
assertEquals(2, f.getDenominator());
f = Fraction.getFraction("0.66666");
assertEquals(2, f.getNumerator());
assertEquals(3, f.getDenominator());
try {
f = Fraction.getFraction("2.3R");
fail("Expecting NumberFormatException");
} catch (NumberFormatException ex) {}
try {
f = Fraction.getFraction("2147483648"); // too big
fail("Expecting NumberFormatException");
} catch (NumberFormatException ex) {}
try {
f = Fraction.getFraction(".");
fail("Expecting NumberFormatException");
} catch (NumberFormatException ex) {}
}
public void testFactory_String_proper() {
Fraction f = null;
f = Fraction.getFraction("0 0/1");
assertEquals(0, f.getNumerator());
assertEquals(1, f.getDenominator());
f = Fraction.getFraction("1 1/5");
assertEquals(6, f.getNumerator());
assertEquals(5, f.getDenominator());
f = Fraction.getFraction("7 1/2");
assertEquals(15, f.getNumerator());
assertEquals(2, f.getDenominator());
f = Fraction.getFraction("1 2/4");
assertEquals(6, f.getNumerator());
assertEquals(4, f.getDenominator());
f = Fraction.getFraction("-7 1/2");
assertEquals(-15, f.getNumerator());
assertEquals(2, f.getDenominator());
f = Fraction.getFraction("-1 2/4");
assertEquals(-6, f.getNumerator());
assertEquals(4, f.getDenominator());
try {
f = Fraction.getFraction("2 3");
fail("expecting NumberFormatException");
} catch (NumberFormatException ex) {}
try {
f = Fraction.getFraction("a 3");
fail("expecting NumberFormatException");
} catch (NumberFormatException ex) {}
try {
f = Fraction.getFraction("2 b/4");
fail("expecting NumberFormatException");
} catch (NumberFormatException ex) {}
try {
f = Fraction.getFraction("2 ");
fail("expecting NumberFormatException");
} catch (NumberFormatException ex) {}
try {
f = Fraction.getFraction(" 3");
fail("expecting NumberFormatException");
} catch (NumberFormatException ex) {}
try {
f = Fraction.getFraction(" ");
fail("expecting NumberFormatException");
} catch (NumberFormatException ex) {}
}
public void testFactory_String_improper() {
Fraction f = null;
f = Fraction.getFraction("0/1");
assertEquals(0, f.getNumerator());
assertEquals(1, f.getDenominator());
f = Fraction.getFraction("1/5");
assertEquals(1, f.getNumerator());
assertEquals(5, f.getDenominator());
f = Fraction.getFraction("1/2");
assertEquals(1, f.getNumerator());
assertEquals(2, f.getDenominator());
f = Fraction.getFraction("2/3");
assertEquals(2, f.getNumerator());
assertEquals(3, f.getDenominator());
f = Fraction.getFraction("7/3");
assertEquals(7, f.getNumerator());
assertEquals(3, f.getDenominator());
f = Fraction.getFraction("2/4");
assertEquals(2, f.getNumerator());
assertEquals(4, f.getDenominator());
try {
f = Fraction.getFraction("2/d");
fail("expecting NumberFormatException");
} catch (NumberFormatException ex) {}
try {
f = Fraction.getFraction("2e/3");
fail("expecting NumberFormatException");
} catch (NumberFormatException ex) {}
try {
f = Fraction.getFraction("2/");
fail("expecting NumberFormatException");
} catch (NumberFormatException ex) {}
try {
f = Fraction.getFraction("/");
fail("expecting NumberFormatException");
} catch (NumberFormatException ex) {}
}
public void testGets() {
Fraction f = null;
f = Fraction.getFraction(3, 5, 6);
assertEquals(23, f.getNumerator());
assertEquals(3, f.getProperWhole());
assertEquals(5, f.getProperNumerator());
assertEquals(6, f.getDenominator());
f = Fraction.getFraction(-3, 5, 6);
assertEquals(-23, f.getNumerator());
assertEquals(-3, f.getProperWhole());
assertEquals(5, f.getProperNumerator());
assertEquals(6, f.getDenominator());
f = Fraction.getFraction(Integer.MIN_VALUE, 0, 1);
assertEquals(Integer.MIN_VALUE, f.getNumerator());
assertEquals(Integer.MIN_VALUE, f.getProperWhole());
assertEquals(0, f.getProperNumerator());
assertEquals(1, f.getDenominator());
}
public void testConversions() {
Fraction f = null;
f = Fraction.getFraction(3, 7, 8);
assertEquals(3, f.intValue());
assertEquals(3L, f.longValue());
assertEquals(3.875f, f.floatValue(), 0.00001f);
assertEquals(3.875d, f.doubleValue(), 0.00001d);
}
public void testReduce() {
Fraction f = null;
f = Fraction.getFraction(50, 75);
f = f.reduce();
assertEquals(2, f.getNumerator());
assertEquals(3, f.getDenominator());
f = Fraction.getFraction(2, 3);
f = f.reduce();
assertEquals(2, f.getNumerator());
assertEquals(3, f.getDenominator());
}
public void testInvert() {
Fraction f = null;
f = Fraction.getFraction(50, 75);
f = f.invert();
assertEquals(75, f.getNumerator());
assertEquals(50, f.getDenominator());
f = Fraction.getFraction(4, 3);
f = f.invert();
assertEquals(3, f.getNumerator());
assertEquals(4, f.getDenominator());
f = Fraction.getFraction(-15, 47);
f = f.invert();
assertEquals(-47, f.getNumerator());
assertEquals(15, f.getDenominator());
f = Fraction.getFraction(0, 3);
try {
f = f.invert();
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
// large values
f = Fraction.getFraction(Integer.MIN_VALUE, 1);
try {
f = f.invert();
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
f = Fraction.getFraction(Integer.MAX_VALUE, 1);
f = f.invert();
assertEquals(1, f.getNumerator());
assertEquals(Integer.MAX_VALUE, f.getDenominator());
}
public void testNegate() {
Fraction f = null;
f = Fraction.getFraction(50, 75);
f = f.negate();
assertEquals(-50, f.getNumerator());
assertEquals(75, f.getDenominator());
f = Fraction.getFraction(-50, 75);
f = f.negate();
assertEquals(50, f.getNumerator());
assertEquals(75, f.getDenominator());
// large values
f = Fraction.getFraction(Integer.MAX_VALUE-1, Integer.MAX_VALUE);
f = f.negate();
assertEquals(Integer.MIN_VALUE+2, f.getNumerator());
assertEquals(Integer.MAX_VALUE, f.getDenominator());
f = Fraction.getFraction(Integer.MIN_VALUE, 1);
try {
f = f.negate();
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
}
public void testAbs() {
Fraction f = null;
f = Fraction.getFraction(50, 75);
f = f.abs();
assertEquals(50, f.getNumerator());
assertEquals(75, f.getDenominator());
f = Fraction.getFraction(-50, 75);
f = f.abs();
assertEquals(50, f.getNumerator());
assertEquals(75, f.getDenominator());
f = Fraction.getFraction(Integer.MAX_VALUE, 1);
f = f.abs();
assertEquals(Integer.MAX_VALUE, f.getNumerator());
assertEquals(1, f.getDenominator());
f = Fraction.getFraction(Integer.MAX_VALUE, -1);
f = f.abs();
assertEquals(Integer.MAX_VALUE, f.getNumerator());
assertEquals(1, f.getDenominator());
f = Fraction.getFraction(Integer.MIN_VALUE, 1);
try {
f = f.abs();
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
}
public void testPow() {
Fraction f = null;
f = Fraction.getFraction(3, 5);
assertEquals(Fraction.ONE, f.pow(0));
f = Fraction.getFraction(3, 5);
assertSame(f, f.pow(1));
assertEquals(f, f.pow(1));
f = Fraction.getFraction(3, 5);
f = f.pow(2);
assertEquals(9, f.getNumerator());
assertEquals(25, f.getDenominator());
f = Fraction.getFraction(3, 5);
f = f.pow(3);
assertEquals(27, f.getNumerator());
assertEquals(125, f.getDenominator());
f = Fraction.getFraction(3, 5);
f = f.pow(-1);
assertEquals(5, f.getNumerator());
assertEquals(3, f.getDenominator());
f = Fraction.getFraction(3, 5);
f = f.pow(-2);
assertEquals(25, f.getNumerator());
assertEquals(9, f.getDenominator());
// check unreduced fractions stay that way.
f = Fraction.getFraction(6, 10);
assertEquals(Fraction.ONE, f.pow(0));
f = Fraction.getFraction(6, 10);
assertEquals(f, f.pow(1));
assertFalse(f.pow(1).equals(Fraction.getFraction(3,5)));
f = Fraction.getFraction(6, 10);
f = f.pow(2);
assertEquals(9, f.getNumerator());
assertEquals(25, f.getDenominator());
f = Fraction.getFraction(6, 10);
f = f.pow(3);
assertEquals(27, f.getNumerator());
assertEquals(125, f.getDenominator());
f = Fraction.getFraction(6, 10);
f = f.pow(-1);
assertEquals(10, f.getNumerator());
assertEquals(6, f.getDenominator());
f = Fraction.getFraction(6, 10);
f = f.pow(-2);
assertEquals(25, f.getNumerator());
assertEquals(9, f.getDenominator());
// zero to any positive power is still zero.
f = Fraction.getFraction(0, 1231);
f = f.pow(1);
assertTrue(0==f.compareTo(Fraction.ZERO));
assertEquals(0, f.getNumerator());
assertEquals(1231, f.getDenominator());
f = f.pow(2);
assertTrue(0==f.compareTo(Fraction.ZERO));
assertEquals(0, f.getNumerator());
assertEquals(1, f.getDenominator());
// zero to negative powers should throw an exception
try {
f = f.pow(-1);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f = f.pow(Integer.MIN_VALUE);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
// one to any power is still one.
f = Fraction.getFraction(1, 1);
f = f.pow(0);
assertEquals(f, Fraction.ONE);
f = f.pow(1);
assertEquals(f, Fraction.ONE);
f = f.pow(-1);
assertEquals(f, Fraction.ONE);
f = f.pow(Integer.MAX_VALUE);
assertEquals(f, Fraction.ONE);
f = f.pow(Integer.MIN_VALUE);
assertEquals(f, Fraction.ONE);
f = Fraction.getFraction(Integer.MAX_VALUE, 1);
try {
f = f.pow(2);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
// Numerator growing too negative during the pow operation.
f = Fraction.getFraction(Integer.MIN_VALUE, 1);
try {
f = f.pow(3);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
f = Fraction.getFraction(65536, 1);
try {
f = f.pow(2);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
}
public void testAdd() {
Fraction f = null;
Fraction f1 = null;
Fraction f2 = null;
f1 = Fraction.getFraction(3, 5);
f2 = Fraction.getFraction(1, 5);
f = f1.add(f2);
assertEquals(4, f.getNumerator());
assertEquals(5, f.getDenominator());
f1 = Fraction.getFraction(3, 5);
f2 = Fraction.getFraction(2, 5);
f = f1.add(f2);
assertEquals(1, f.getNumerator());
assertEquals(1, f.getDenominator());
f1 = Fraction.getFraction(3, 5);
f2 = Fraction.getFraction(3, 5);
f = f1.add(f2);
assertEquals(6, f.getNumerator());
assertEquals(5, f.getDenominator());
f1 = Fraction.getFraction(3, 5);
f2 = Fraction.getFraction(-4, 5);
f = f1.add(f2);
assertEquals(-1, f.getNumerator());
assertEquals(5, f.getDenominator());
f1 = Fraction.getFraction(Integer.MAX_VALUE - 1, 1);
f2 = Fraction.ONE;
f = f1.add(f2);
assertEquals(Integer.MAX_VALUE, f.getNumerator());
assertEquals(1, f.getDenominator());
f1 = Fraction.getFraction(3, 5);
f2 = Fraction.getFraction(1, 2);
f = f1.add(f2);
assertEquals(11, f.getNumerator());
assertEquals(10, f.getDenominator());
f1 = Fraction.getFraction(3, 8);
f2 = Fraction.getFraction(1, 6);
f = f1.add(f2);
assertEquals(13, f.getNumerator());
assertEquals(24, f.getDenominator());
f1 = Fraction.getFraction(0, 5);
f2 = Fraction.getFraction(1, 5);
f = f1.add(f2);
assertSame(f2, f);
f = f2.add(f1);
assertSame(f2, f);
f1 = Fraction.getFraction(-1, 13*13*2*2);
f2 = Fraction.getFraction(-2, 13*17*2);
f = f1.add(f2);
assertEquals(13*13*17*2*2, f.getDenominator());
assertEquals(-17 - 2*13*2, f.getNumerator());
try {
f.add(null);
fail("expecting IllegalArgumentException");
} catch (IllegalArgumentException ex) {}
// if this fraction is added naively, it will overflow.
// check that it doesn't.
f1 = Fraction.getFraction(1,32768*3);
f2 = Fraction.getFraction(1,59049);
f = f1.add(f2);
assertEquals(52451, f.getNumerator());
assertEquals(1934917632, f.getDenominator());
f1 = Fraction.getFraction(Integer.MIN_VALUE, 3);
f2 = Fraction.ONE_THIRD;
f = f1.add(f2);
assertEquals(Integer.MIN_VALUE+1, f.getNumerator());
assertEquals(3, f.getDenominator());
f1 = Fraction.getFraction(Integer.MAX_VALUE - 1, 1);
f2 = Fraction.ONE;
f = f1.add(f2);
assertEquals(Integer.MAX_VALUE, f.getNumerator());
assertEquals(1, f.getDenominator());
try {
f = f.add(Fraction.ONE); // should overflow
fail("expecting ArithmeticException but got: " + f.toString());
} catch (ArithmeticException ex) {}
// denominator should not be a multiple of 2 or 3 to trigger overflow
f1 = Fraction.getFraction(Integer.MIN_VALUE, 5);
f2 = Fraction.getFraction(-1,5);
try {
f = f1.add(f2); // should overflow
fail("expecting ArithmeticException but got: " + f.toString());
} catch (ArithmeticException ex) {}
try {
f= Fraction.getFraction(-Integer.MAX_VALUE, 1);
f = f.add(f);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f= Fraction.getFraction(-Integer.MAX_VALUE, 1);
f = f.add(f);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
f1 = Fraction.getFraction(3,327680);
f2 = Fraction.getFraction(2,59049);
try {
f = f1.add(f2); // should overflow
fail("expecting ArithmeticException but got: " + f.toString());
} catch (ArithmeticException ex) {}
}
public void testSubtract() {
Fraction f = null;
Fraction f1 = null;
Fraction f2 = null;
f1 = Fraction.getFraction(3, 5);
f2 = Fraction.getFraction(1, 5);
f = f1.subtract(f2);
assertEquals(2, f.getNumerator());
assertEquals(5, f.getDenominator());
f1 = Fraction.getFraction(7, 5);
f2 = Fraction.getFraction(2, 5);
f = f1.subtract(f2);
assertEquals(1, f.getNumerator());
assertEquals(1, f.getDenominator());
f1 = Fraction.getFraction(3, 5);
f2 = Fraction.getFraction(3, 5);
f = f1.subtract(f2);
assertEquals(0, f.getNumerator());
assertEquals(1, f.getDenominator());
f1 = Fraction.getFraction(3, 5);
f2 = Fraction.getFraction(-4, 5);
f = f1.subtract(f2);
assertEquals(7, f.getNumerator());
assertEquals(5, f.getDenominator());
f1 = Fraction.getFraction(0, 5);
f2 = Fraction.getFraction(4, 5);
f = f1.subtract(f2);
assertEquals(-4, f.getNumerator());
assertEquals(5, f.getDenominator());
f1 = Fraction.getFraction(0, 5);
f2 = Fraction.getFraction(-4, 5);
f = f1.subtract(f2);
assertEquals(4, f.getNumerator());
assertEquals(5, f.getDenominator());
f1 = Fraction.getFraction(3, 5);
f2 = Fraction.getFraction(1, 2);
f = f1.subtract(f2);
assertEquals(1, f.getNumerator());
assertEquals(10, f.getDenominator());
f1 = Fraction.getFraction(0, 5);
f2 = Fraction.getFraction(1, 5);
f = f2.subtract(f1);
assertSame(f2, f);
try {
f.subtract(null);
fail("expecting IllegalArgumentException");
} catch (IllegalArgumentException ex) {}
// if this fraction is subtracted naively, it will overflow.
// check that it doesn't.
f1 = Fraction.getFraction(1,32768*3);
f2 = Fraction.getFraction(1,59049);
f = f1.subtract(f2);
assertEquals(-13085, f.getNumerator());
assertEquals(1934917632, f.getDenominator());
f1 = Fraction.getFraction(Integer.MIN_VALUE, 3);
f2 = Fraction.ONE_THIRD.negate();
f = f1.subtract(f2);
assertEquals(Integer.MIN_VALUE+1, f.getNumerator());
assertEquals(3, f.getDenominator());
f1 = Fraction.getFraction(Integer.MAX_VALUE, 1);
f2 = Fraction.ONE;
f = f1.subtract(f2);
assertEquals(Integer.MAX_VALUE-1, f.getNumerator());
assertEquals(1, f.getDenominator());
try {
f1 = Fraction.getFraction(1, Integer.MAX_VALUE);
f2 = Fraction.getFraction(1, Integer.MAX_VALUE - 1);
f = f1.subtract(f2);
fail("expecting ArithmeticException"); //should overflow
} catch (ArithmeticException ex) {}
// denominator should not be a multiple of 2 or 3 to trigger overflow
f1 = Fraction.getFraction(Integer.MIN_VALUE, 5);
f2 = Fraction.getFraction(1,5);
try {
f = f1.subtract(f2); // should overflow
fail("expecting ArithmeticException but got: " + f.toString());
} catch (ArithmeticException ex) {}
try {
f= Fraction.getFraction(Integer.MIN_VALUE, 1);
f = f.subtract(Fraction.ONE);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f= Fraction.getFraction(Integer.MAX_VALUE, 1);
f = f.subtract(Fraction.ONE.negate());
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
f1 = Fraction.getFraction(3,327680);
f2 = Fraction.getFraction(2,59049);
try {
f = f1.subtract(f2); // should overflow
fail("expecting ArithmeticException but got: " + f.toString());
} catch (ArithmeticException ex) {}
}
public void testMultiply() {
Fraction f = null;
Fraction f1 = null;
Fraction f2 = null;
f1 = Fraction.getFraction(3, 5);
f2 = Fraction.getFraction(2, 5);
f = f1.multiplyBy(f2);
assertEquals(6, f.getNumerator());
assertEquals(25, f.getDenominator());
f1 = Fraction.getFraction(6, 10);
f2 = Fraction.getFraction(6, 10);
f = f1.multiplyBy(f2);
assertEquals(9, f.getNumerator());
assertEquals(25, f.getDenominator());
f = f.multiplyBy(f2);
assertEquals(27, f.getNumerator());
assertEquals(125, f.getDenominator());
f1 = Fraction.getFraction(3, 5);
f2 = Fraction.getFraction(-2, 5);
f = f1.multiplyBy(f2);
assertEquals(-6, f.getNumerator());
assertEquals(25, f.getDenominator());
f1 = Fraction.getFraction(-3, 5);
f2 = Fraction.getFraction(-2, 5);
f = f1.multiplyBy(f2);
assertEquals(6, f.getNumerator());
assertEquals(25, f.getDenominator());
f1 = Fraction.getFraction(0, 5);
f2 = Fraction.getFraction(2, 7);
f = f1.multiplyBy(f2);
assertSame(Fraction.ZERO, f);
f1 = Fraction.getFraction(2, 7);
f2 = Fraction.ONE;
f = f1.multiplyBy(f2);
assertEquals(2, f.getNumerator());
assertEquals(7, f.getDenominator());
f1 = Fraction.getFraction(Integer.MAX_VALUE, 1);
f2 = Fraction.getFraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
f = f1.multiplyBy(f2);
assertEquals(Integer.MIN_VALUE, f.getNumerator());
assertEquals(1, f.getDenominator());
try {
f.multiplyBy(null);
fail("expecting IllegalArgumentException");
} catch (IllegalArgumentException ex) {}
try {
f1 = Fraction.getFraction(1, Integer.MAX_VALUE);
f = f1.multiplyBy(f1); // should overflow
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f1 = Fraction.getFraction(1, -Integer.MAX_VALUE);
f = f1.multiplyBy(f1); // should overflow
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
}
public void testDivide() {
Fraction f = null;
Fraction f1 = null;
Fraction f2 = null;
f1 = Fraction.getFraction(3, 5);
f2 = Fraction.getFraction(2, 5);
f = f1.divideBy(f2);
assertEquals(3, f.getNumerator());
assertEquals(2, f.getDenominator());
f1 = Fraction.getFraction(3, 5);
f2 = Fraction.ZERO;
try {
f = f1.divideBy(f2);
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
f1 = Fraction.getFraction(0, 5);
f2 = Fraction.getFraction(2, 7);
f = f1.divideBy(f2);
assertSame(Fraction.ZERO, f);
f1 = Fraction.getFraction(2, 7);
f2 = Fraction.ONE;
f = f1.divideBy(f2);
assertEquals(2, f.getNumerator());
assertEquals(7, f.getDenominator());
f1 = Fraction.getFraction(1, Integer.MAX_VALUE);
f = f1.divideBy(f1);
assertEquals(1, f.getNumerator());
assertEquals(1, f.getDenominator());
f1 = Fraction.getFraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
f2 = Fraction.getFraction(1, Integer.MAX_VALUE);
f = f1.divideBy(f2);
assertEquals(Integer.MIN_VALUE, f.getNumerator());
assertEquals(1, f.getDenominator());
try {
f.divideBy(null);
fail("IllegalArgumentException");
} catch (IllegalArgumentException ex) {}
try {
f1 = Fraction.getFraction(1, Integer.MAX_VALUE);
f = f1.divideBy(f1.invert()); // should overflow
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
try {
f1 = Fraction.getFraction(1, -Integer.MAX_VALUE);
f = f1.divideBy(f1.invert()); // should overflow
fail("expecting ArithmeticException");
} catch (ArithmeticException ex) {}
}
public void testEquals() {
Fraction f1 = null;
Fraction f2 = null;
f1 = Fraction.getFraction(3, 5);
assertEquals(false, f1.equals(null));
assertEquals(false, f1.equals(new Object()));
assertEquals(false, f1.equals(new Integer(6)));
f1 = Fraction.getFraction(3, 5);
f2 = Fraction.getFraction(2, 5);
assertEquals(false, f1.equals(f2));
assertEquals(true, f1.equals(f1));
assertEquals(true, f2.equals(f2));
f2 = Fraction.getFraction(3, 5);
assertEquals(true, f1.equals(f2));
f2 = Fraction.getFraction(6, 10);
assertEquals(false, f1.equals(f2));
}
public void testHashCode() {
Fraction f1 = Fraction.getFraction(3, 5);
Fraction f2 = Fraction.getFraction(3, 5);
assertTrue(f1.hashCode() == f2.hashCode());
f2 = Fraction.getFraction(2, 5);
assertTrue(f1.hashCode() != f2.hashCode());
f2 = Fraction.getFraction(6, 10);
assertTrue(f1.hashCode() != f2.hashCode());
}
public void testCompareTo() {
Fraction f1 = null;
Fraction f2 = null;
f1 = Fraction.getFraction(3, 5);
assertTrue(f1.compareTo(f1) == 0);
try {
f1.compareTo(null);
fail("expecting NullPointerException");
} catch (NullPointerException ex) {}
try {
f1.compareTo(new Object());
fail("expecting ClassCastException");
} catch (ClassCastException ex) {}
f2 = Fraction.getFraction(2, 5);
assertTrue(f1.compareTo(f2) > 0);
assertTrue(f2.compareTo(f2) == 0);
f2 = Fraction.getFraction(4, 5);
assertTrue(f1.compareTo(f2) < 0);
assertTrue(f2.compareTo(f2) == 0);
f2 = Fraction.getFraction(3, 5);
assertTrue(f1.compareTo(f2) == 0);
assertTrue(f2.compareTo(f2) == 0);
f2 = Fraction.getFraction(6, 10);
assertTrue(f1.compareTo(f2) == 0);
assertTrue(f2.compareTo(f2) == 0);
f2 = Fraction.getFraction(-1, 1, Integer.MAX_VALUE);
assertTrue(f1.compareTo(f2) > 0);
assertTrue(f2.compareTo(f2) == 0);
}
public void testToString() {
Fraction f = null;
f = Fraction.getFraction(3, 5);
String str = f.toString();
assertEquals("3/5", str);
assertSame(str, f.toString());
f = Fraction.getFraction(7, 5);
assertEquals("7/5", f.toString());
f = Fraction.getFraction(4, 2);
assertEquals("4/2", f.toString());
f = Fraction.getFraction(0, 2);
assertEquals("0/2", f.toString());
f = Fraction.getFraction(2, 2);
assertEquals("2/2", f.toString());
f = Fraction.getFraction(Integer.MIN_VALUE, 0, 1);
assertEquals("-2147483648/1", f.toString());
f = Fraction.getFraction(-1, 1, Integer.MAX_VALUE);
assertEquals("-2147483648/2147483647", f.toString());
}
public void testToProperString() {
Fraction f = null;
f = Fraction.getFraction(3, 5);
String str = f.toProperString();
assertEquals("3/5", str);
assertSame(str, f.toProperString());
f = Fraction.getFraction(7, 5);
assertEquals("1 2/5", f.toProperString());
f = Fraction.getFraction(14, 10);
assertEquals("1 4/10", f.toProperString());
f = Fraction.getFraction(4, 2);
assertEquals("2", f.toProperString());
f = Fraction.getFraction(0, 2);
assertEquals("0", f.toProperString());
f = Fraction.getFraction(2, 2);
assertEquals("1", f.toProperString());
f = Fraction.getFraction(-7, 5);
assertEquals("-1 2/5", f.toProperString());
f = Fraction.getFraction(Integer.MIN_VALUE, 0, 1);
assertEquals("-2147483648", f.toProperString());
f = Fraction.getFraction(-1, 1, Integer.MAX_VALUE);
assertEquals("-1 1/2147483647", f.toProperString());
}
}
Three are included in the Framework:
Swing: junit.swingui.TestRunner
AWT: junit.awtui.TestRunner
Text: junit.textui.TestRunner
You can write your own
Use the one of the test runners to run the current class:
public static void main(String[] args) {
junit.textui.TestRunner runner = new junit.textui.TestRunner();
runner.run(FractionTest.class);
}
Add a main method to your test class and use it to run the tests.
public static void main(String[] args) {
TestRunner.run(FractionTest.class);
}
Let's add a method that squares a fraction.
Write the test first.
This describes what the method should do, and how it should do it (i.e. what the API should look like.
Initially, this test won't even compile.
The design of the
Fraction
class requires
the object to be immutable
public void testSquare() {
Fraction a = Fraction.getFraction(1, 2);
Fraction b = a.square();
assertEquals(b, Fraction.ONE_QUARTER);
}
The design of the Fraction
class requires the object to be immutable.
The simplest thing that could possibly work:
public Fraction square() {
return Fraction.ONE_QUARTER;
}
~/documents/speaking/starwest/junit/lib/commons-math-1.0-src$ maven jar
__ __
| \/ |__ _Apache__ ___
| |\/| / _` \ V / -_) ' \ ~ intelligent projects ~
|_| |_\__,_|\_/\___|_||_| v. 1.0.2
build:start:
java:prepare-filesystem:
[mkdir] Created dir: /Users/elharo/Documents/speaking/starwest/junit/lib/commons-math-1.0-src/target/classes
java:compile:
[echo] Compiling to /Users/elharo/Documents/speaking/starwest/junit/lib/commons-math-1.0-src/target/classes
[echo]
==========================================================
NOTE: Targetting JVM 1.4, classes
will not run on earlier JVMs
==========================================================
[javac] Compiling 106 source files to /Users/elharo/Documents/speaking/starwest/junit/lib/commons-math-1.0-src/target/classes
java:jar-resources:
test:prepare-filesystem:
[mkdir] Created dir: /Users/elharo/Documents/speaking/starwest/junit/lib/commons-math-1.0-src/target/test-classes
[mkdir] Created dir: /Users/elharo/Documents/speaking/starwest/junit/lib/commons-math-1.0-src/target/test-reports
test:test-resources:
Copying 10 files to /Users/elharo/Documents/speaking/starwest/junit/lib/commons-math-1.0-src/target/test-classes
test:compile:
[javac] Compiling 89 source files to /Users/elharo/Documents/speaking/starwest/junit/lib/commons-math-1.0-src/target/test-classes
test:test:
[junit] Running org.apache.commons.math.analysis.BisectionSolverTest
[junit] Tests run: 11, Failures: 0, Errors: 0, Time elapsed: 0.409 sec
[junit] Running org.apache.commons.math.analysis.BrentSolverTest
[junit] Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 0.402 sec
[junit] Running org.apache.commons.math.analysis.ConvergenceExceptionTest
[junit] Tests run: 4, Failures: 0, Errors: 0, Time elapsed: 0.343 sec
[junit] Running org.apache.commons.math.analysis.NewtonSolverTest
[junit] Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 0.357 sec
[junit] Running org.apache.commons.math.analysis.PolynomialFunctionTest
[junit] Tests run: 5, Failures: 0, Errors: 0, Time elapsed: 0.371 sec
[junit] Running org.apache.commons.math.analysis.PolynomialSplineFunctionTest
[junit] Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 0.347 sec
[junit] Running org.apache.commons.math.analysis.SplineInterpolatorTest
[junit] Tests run: 5, Failures: 0, Errors: 0, Time elapsed: 0.333 sec
[junit] Running org.apache.commons.math.analysis.UnivariateRealSolverFactoryImplTest
[junit] Tests run: 8, Failures: 0, Errors: 0, Time elapsed: 0.354 sec
[junit] Running org.apache.commons.math.analysis.UnivariateRealSolverUtilsTest
[junit] Tests run: 9, Failures: 0, Errors: 0, Time elapsed: 0.391 sec
[junit] Running org.apache.commons.math.complex.ComplexFormatTest
[junit] Tests run: 34, Failures: 0, Errors: 0, Time elapsed: 0.365 sec
[junit] Running org.apache.commons.math.complex.ComplexTest
[junit] Tests run: 22, Failures: 0, Errors: 0, Time elapsed: 0.333 sec
[junit] Running org.apache.commons.math.complex.ComplexUtilsTest
[junit] Tests run: 33, Failures: 0, Errors: 0, Time elapsed: 0.362 sec
[junit] Running org.apache.commons.math.complex.FrenchComplexFormatTest
[junit] Tests run: 34, Failures: 0, Errors: 0, Time elapsed: 0.396 sec
[junit] Running org.apache.commons.math.distribution.BinomialDistributionTest
[junit] Tests run: 6, Failures: 0, Errors: 0, Time elapsed: 0.468 sec
[junit] Running org.apache.commons.math.distribution.ChiSquareDistributionTest
[junit] Tests run: 6, Failures: 0, Errors: 0, Time elapsed: 0.446 sec
[junit] Running org.apache.commons.math.distribution.DistributionFactoryImplTest
[junit] Tests run: 32, Failures: 0, Errors: 0, Time elapsed: 0.429 sec
[junit] Running org.apache.commons.math.distribution.ExponentialDistributionTest
[junit] Tests run: 8, Failures: 0, Errors: 0, Time elapsed: 0.422 sec
[junit] Running org.apache.commons.math.distribution.FDistributionTest
[junit] Tests run: 8, Failures: 0, Errors: 0, Time elapsed: 0.448 sec
[junit] Running org.apache.commons.math.distribution.GammaDistributionTest
[junit] Tests run: 8, Failures: 0, Errors: 0, Time elapsed: 0.479 sec
[junit] Running org.apache.commons.math.distribution.HypergeometricDistributionTest
[junit] Tests run: 7, Failures: 0, Errors: 0, Time elapsed: 0.425 sec
[junit] Running org.apache.commons.math.distribution.NormalDistributionTest
[junit] Tests run: 10, Failures: 0, Errors: 0, Time elapsed: 0.451 sec
[junit] Running org.apache.commons.math.distribution.PoissonDistributionTest
[junit] Tests run: 6, Failures: 0, Errors: 0, Time elapsed: 0.43 sec
[junit] Running org.apache.commons.math.distribution.TDistributionTest
[junit] Tests run: 8, Failures: 0, Errors: 0, Time elapsed: 0.465 sec
[junit] Running org.apache.commons.math.FunctionEvaluationExceptionTest
[junit] Tests run: 3, Failures: 0, Errors: 0, Time elapsed: 0.336 sec
[junit] Running org.apache.commons.math.linear.BigMatrixImplTest
[junit] Tests run: 26, Failures: 0, Errors: 0, Time elapsed: 0.591 sec
[junit] Running org.apache.commons.math.linear.InvalidMatrixExceptionTest
[junit] Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 0.35 sec
[junit] Running org.apache.commons.math.linear.MatrixIndexExceptionTest
[junit] Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 0.34 sec
[junit] Running org.apache.commons.math.linear.MatrixUtilsTest
[junit] Tests run: 6, Failures: 0, Errors: 0, Time elapsed: 0.352 sec
[junit] Running org.apache.commons.math.linear.RealMatrixImplTest
[junit] Tests run: 27, Failures: 0, Errors: 0, Time elapsed: 0.359 sec
[junit] Running org.apache.commons.math.MathConfigurationExceptionTest
[junit] Tests run: 4, Failures: 0, Errors: 0, Time elapsed: 0.35 sec
[junit] Running org.apache.commons.math.MathExceptionTest
[junit] Tests run: 6, Failures: 0, Errors: 0, Time elapsed: 0.411 sec
[junit] Running org.apache.commons.math.random.EmpiricalDistributionTest
[junit] Tests run: 7, Failures: 0, Errors: 0, Time elapsed: 4.055 sec
[junit] Running org.apache.commons.math.random.RandomDataTest
[junit] Tests run: 13, Failures: 0, Errors: 0, Time elapsed: 1.243 sec
[junit] Running org.apache.commons.math.random.ValueServerTest
...
[junit] Tests run: 6, Failures: 0, Errors: 0, Time elapsed: 0.37 sec
[junit] Running org.apache.commons.math.stat.regression.SimpleRegressionTest
[junit] Tests run: 8, Failures: 0, Errors: 0, Time elapsed: 0.452 sec
[junit] Running org.apache.commons.math.stat.StatUtilsTest
[junit] Tests run: 13, Failures: 0, Errors: 0, Time elapsed: 0.363 sec
[junit] Running org.apache.commons.math.util.ContinuedFractionTest
[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.331 sec
[junit] Running org.apache.commons.math.util.DefaultTransformerTest
[junit] Tests run: 6, Failures: 0, Errors: 0, Time elapsed: 0.339 sec
[junit] Running org.apache.commons.math.util.MathUtilsTest
[junit] Tests run: 23, Failures: 0, Errors: 0, Time elapsed: 0.339 sec
[junit] Running org.apache.commons.math.util.ResizableDoubleArrayTest
[junit] Tests run: 11, Failures: 0, Errors: 0, Time elapsed: 0.361 sec
[junit] Running org.apache.commons.math.util.TransformerMapTest
[junit] Tests run: 7, Failures: 0, Errors: 0, Time elapsed: 0.338 sec
jar:jar:
[jar] Building jar: /Users/elharo/Documents/speaking/starwest/junit/lib/commons-math-1.0-src/target/commons-math-1.0.jar
public void testSquareOneThird() {
Fraction a = Fraction.getFraction(1, 3);
Fraction b = a.square();
assertEquals(b, Fraction.getFraction(1, 9));
}
public Fraction square() {
return getFraction(numerator*numerator, denominator*denominator);
}
public interface TestListener {
void addError(Test test, Throwable t);
void addFailure(Test test, AssertionFailedError t);
void endTest(Test test);
void startTest(Test test);
}
Ant
Maven
Eclipse
<property name="test.outputFormat" value="xml"/>
<target name="test" depends="compile"
description="Run JUnit tests using command line user interface">
<junit printsummary="off" fork="yes">
<classpath refid="test.class.path" />
<formatter type="${test.outputFormat}" />
<batchtest fork="yes" todir="${testoutput.dir}">
<fileset dir="${build.src}">
<include name="**/*Test.java" />
<exclude name="**/pantry/*" />
<exclude name="**/MegaTest.java" />
<exclude name="**/benchmarks/*.java" />
<exclude name="**/EBCDICTest.java" />
</fileset>
</batchtest>
</junit>
<junitreport todir="${testoutput.dir}">
<fileset dir="${testoutput.dir}">
<include name="TEST-*.xml" />
</fileset>
<report todir="${testoutput.dir}" />
</junitreport>
</target>
<build>
<nagEmailAddress>dev@jaxen.codehaus.org</nagEmailAddress>
<sourceDirectory>src/java/main</sourceDirectory>
<unitTestSourceDirectory>src/java/test</unitTestSourceDirectory>
<!-- Unit test classes -->
<unitTest>
<includes>
<include>**/*Test.java</include>
</includes>
<excludes>
<!-- currently broken -->
<exclude>org/jaxen/jdom/XPathTest.java</exclude>
</excludes>
</unitTest>
</build>
Sample HTML outputTest classes and methods must be public.
However they are generally not published.
Put the tests in a separate package, and don't include that package when producing JavaDoc.
Fixtures can do too much work.
Push fields into local variables.
Break up test classes into smaller classes that share the same fixtures
How much coverage is enough?
Cobertura
Local Databases
Mock Objects
URL Class
HttpUnit
Local Servers
Mocking Servers
Pragmatic Unit Testing in Java with JUnit
by Andy Hunt and Dave Thomas
JUnit Recipes by J. B. Rainsberger; Manning, 2004; ISBN 1932394230
This presentation: http://www.cafeaulait.org/slides/starwest/junit/
JUnit: http://www.junit.org/