"

Example: Test a class

Here is an example of a TestNG test class for the Fraction class. Read through the comments carefully, as they explain the methods.

There are a few guidelines that are useful to stick to as you develop a test class:

  • Use comments to list the tests you want to write. You may find that you can combine tests or are missing test cases. If you list the tests first, you can save yourself some development time by properly designing test cases. It also allows you to focus on WHAT needs to be tested before you start focusing on HOW to test.
  • Write one test method at a time. Using Test-Driven Development, you would write the tests for a method before you write the method. Then use the tests you wrote to verify your implementation as you implement the actual method. For instance, you would write testToString before you write toString. Then you can use your tests to ensure that toString returns the correct values.
  • Always use fresh (new) objects in your tests. Tests in testing frameworks are not guaranteed to be executed in any particular order. You don’t want something going wrong in an earlier test to mess up a later test. You can use an external method to create the objects, if you need the objects to be set up in a particular way for the tests, and just call that method in your test method.

/*
 * Tests Fraction class
 *
 * Behavior is expected to match JavaDocs from 
 * Fraction class
 */
public class FractionTester {
    // Each method needs to marked as a test method with @Test
    //
    // Most test are set up as:
    // 1. Create object to use as test object. Each method should use a NEW object.
    // 2. Call method(s) you want to test or are using to test something.
    // 3. Check method's return value and side effects (if they exist and possible
    // to test)

    //////////////////////
    // Test Constructor //
    //////////////////////

    // Test Fraction can be constructed
    @Test
    public void testConstructor() {
        // create a test object
        // these values shouldn't trip up reduce
        Fraction testObj = new Fraction(1, 1);

        // expected value
        String expected = "1/1";

        // assert the return value of toString matches expected
        assertEquals(testObj.toString(), expected);
    }

    //////////////////////////////////
    // Test getters
    // Could you test both in one method? Yes.
    // The benefit to testing in different methods is that
    // if testGetNumerator fails, you know getNumerator is the problem.
    // If you put both in the same method, you'd have to spend a minute
    // investigating to see which of the getters failed.
    // Is that hard? No, not for these particular methods.
    // Could it be hard? Maybe, depending on how complicated the methods are.
    //////////////////////////////////
    @Test
    public void testGetNumerator() {
        // Create test object
        int numerator = 3;
        int denominator = 4;
        Fraction testObj = new Fraction(numerator, denominator);

        // Call getNumerator
        // Assert return value is correct
        assertEquals(testObj.getNumerator(), numerator);
    }

    @Test
    public void testGetDenominator() {
        // Create test object
        int numerator = 23;
        int denominator = 100;
        Fraction testObj = new Fraction(numerator, denominator);

        // Call getDenominator
        // Assert return value is correct
        assertEquals(testObj.getDenominator(), denominator);
    }

    //////////////////////////////////
    // Test reduce
    //
    // Because reduce is private, we have to test reduce indirectly.
    // This breaks black box testing, though. The correct way to think
    // of this is that we're testing that the fraction ends up reduced
    // when we construct the fraction.
    //
    // In this case, we have an option of how we want to test the values.
    // We can use the getters or toString.
    // We can only use toString because it contains the values of the
    // variables we want to check.
    //////////////////////////////////

    /**
     * There are several test cases:
     * 1) already reduced fraction
     * 2) improper fraction (like 18/3)
     * 3) quickly reduceable fraction (like 2/4)
     * 4) not so quickly reduceable fraction (like 405/450)
     * 
     * There is one invalid case: the denominator is zero. This will
     * cause an error, as it should, but if you were designing this class,
     * you may want to spend some time thinking about if you wanted to
     * catch this error and redirect the error message or just let it fail
     * and let Java produce error messages for you.
     */

    @Test
    public void testReducedFraction() {
        Fraction testObj = new Fraction(7, 15);

        int expectedNumerator = 7;
        int expectedDenominator = 15;

        assertEquals(testObj.getNumerator(), expectedNumerator);
        assertEquals(testObj.getDenominator(), expectedDenominator);
    }

    @Test
    public void testImproperFraction() {
        Fraction testObj = new Fraction(18,3);

        int expectedNumerator = 6;
        int expectedDenominator = 1;

        assertEquals(testObj.getNumerator(), expectedNumerator);
        assertEquals(testObj.getDenominator(), expectedDenominator);
    }

    @Test
    public void testQuickReduce() {
        Fraction testObj = new Fraction(2,4);

        int expectedNumerator = 1;
        int expectedDenominator = 2;

        assertEquals(testObj.getNumerator(), expectedNumerator);
        assertEquals(testObj.getDenominator(), expectedDenominator);
    }

    @Test
    public void testSlowReduce() {
        Fraction testObj = new Fraction(405,450);

        int expectedNumerator = 9;
        int expectedDenominator = 10;

        assertEquals(testObj.getNumerator(), expectedNumerator);
        assertEquals(testObj.getDenominator(), expectedDenominator);
    }

    //////////////////////////////////
    // Test toString
    // Is this test redundant? Yes, but redundancy is not always bad.
    // If this test passes, but constructor test doesn't, then you can rule
    // out toString as the location of the bug.
    //////////////////////////////////
    @Test
    public void testToString() {
        Fraction testObj = new Fraction(1,4);

        String expected = "1/4";

        assertEquals(testObj.toString(), expected);
    }

}


 

License

Icon for the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License

Computer Science II Copyright © by Various is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License, except where otherwise noted.