JUnit: A Hands-On Introduction for Testers


JUnit: A Hands-On Introduction for Testers

Elliotte Rusty Harold

Tuesday, November 15, 2005

elharo@metalab.unc.edu

http://www.cafeaulait.org/


Different Kinds of Tests


Different Styles of Testing


When To Write the Tests


Or


Benefits of Test Driven Development


Why test?

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.

For Unit Testing to Work


Micro Test Driven Development

  1. Write a test for a feature

  2. Code the simplest thing that can possibly work

  3. Run the test

  4. Test passed ? goto 1 : goto 2


Everything is preceded by a test


Macro Test Driven Development


Test Driven Testing


JUnit


A class to test: The Fraction Class


Test Classes


Exercise 1: Compile and run the simple addition test


What else do we need to test?

?

Exercise 2: Expanding the test suite


Unit Testing


Reinitialization Example


Consequences


static data is not reinitialized

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

Fixtures


Exercise 3: Adding a Fixture

Move the shared code into fields. Write a setUp() method that initializes these fields.


Answer 3: Adding a Fixture

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

Freeing resources after each test


Constructors for TestCase


Exercise 4: Adding a Constructor

Add a constructor to your test class.


Answer 4: Adding a Constructor

    public FractionTest() {}

    public FractionTest(String name) {
        super(name);
    }

Assertion Messages


Exercise 5: Messages

Add assertion messages to each test.


Answer 5: Assertion Messages

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

Floating point assertions

As always with floating point arithmetic, you want to avoid direct equality comparisons.


Exercise 6: Floating point test


Answer 6: Floating point test

    public void testDoubleValue() {
        Fraction a = Fraction.getFraction(5, 3);
        assertEquals(5.0/3.0, a.doubleValue(), 0.000000001);
    }

Integer assertions

Straight-forward because integer comparisons are straight-forward:


Exercise 7: Integer test


Answer 7: Integer test

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

Integration Tests


Exercise 8: Data Driven TestCase

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.


Answer 8: Data Driven Test


Data Driven Test Issues


Answer 8 Improved: Data Driven Test

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

Object comparisons: asserting object equality


Object comparisons: asserting object identity


Asserting Truth


Asserting Falsity


Asserting Nullness

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

Asserting Non-Nullness

    public void testToString() {
        assertNotNull(list.toString());
    }

Deliberately failing a test


Tests that throw Exceptions


Testing exceptional conditions

    public void testCantRetrieveFromEmptyList() {
     
        try {
            list.getFirst();
            fail("Got Something from an empty list");
        }
        catch (NoSuchElementException success) {
        }
        
    }

Exercise 9: Exploring an API through Tests

Without looking at the JavaDoc, can you add null to a Fraction? What happens if you do? i.e.

Write a test that determines the answer to this question, and then adjust it to match the behavior.


Answer 9: Testing Exceptional Conditions

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

Testing Files


Faking Input

  1. Write sample data onto a ByteArrayOutputStream.

  2. Convert to byte array

  3. Read from a ByteArrayInputStream


Testing Output


Exercise 10: Serialization

Fraction implements Serializable. Write a test that serializes and deserializes a Fraction object; That is:

  1. Create a Fraction

  2. Create a ByteArrayOutputStream

  3. Chain an ObjectOutputStream to the ByteArrayOutputStream

  4. Write the Fraction object onto the ObjectOutputStream using writeObject()

  5. Close the ObjectOutputStream

  6. Get a byte array from the ByteArrayOutputStream using toByteArray()

  7. Make a ByteArrayInputStream from the byte array

  8. Chain an ObjectInputStream to the ByteArrayInputStream

  9. Read the object from the ObjectInputStream using readObject()

  10. Verify that the object is equal to but not the same as the original object


Answer 10: Testing I/O

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

Test Suites


Test Suites

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

Test Suites

Multiple test methods can also be combined in a test suite class:


Test Suites Can Be Combined Into Larger Suites

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

Testing Fields


Testing Protected Methods


Testing Abstract Classes


Testing Package Protected Methods


Testing Private Methods


Testing Interfaces


Data driven tests


Parameterized TestCase Example

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

What to Test


What Not to Test


Exploring the JavaDoc for things to test


Group Exercise: Reading the JavaDoc

?

The Real FractionTest Class

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

TestRunners


Main method for TestCase

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

Exercise 11: Adding a main method

Add a main method to your test class and use it to run the tests.


Answer 11: Adding a main method

    public static void main(String[] args) {
      TestRunner.run(FractionTest.class);
    }

Exercise 12: Test First Development


Answer 12: Writing the Test First

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

Answer 12: Changing the Code

    public Fraction square() {
        return Fraction.ONE_QUARTER;
    }

Answer 12: Compiling with Maven

~/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

Answer 12: Writing Another Test

    public void testSquareOneThird() {
        Fraction a = Fraction.getFraction(1, 3);
        Fraction b = a.square();
        assertEquals(b, Fraction.getFraction(1, 9));
    }

Answer 12: Changing the Code Again

 
    public Fraction square() {
        return getFraction(numerator*numerator, denominator*denominator);
    }

TestListeners

public interface TestListener {

 void 	addError(Test test, Throwable t);
 void 	addFailure(Test test, AssertionFailedError t);
 void 	endTest(Test test);
 void 	startTest(Test test);
 
}

Writing Your Own TestListener


Writing Your Own TestRunner


Integrating Tests into the Build Process


Ant

<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>

Maven

  <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 output

Excluding Tests from the JavaDoc


Test Suite Performance


Profiling tests


Smoke tests


Unnecessary Initialization


Measuring Test Coverage


Testing Databases


Testing Web Servers and Services


To Learn More


Index | Cafe con Leche | Cafe au Lait

Copyright 2005 Elliotte Rusty Harold
elharo@metalab.unc.edu
Last Modified September 6, 2005