Fading Coder

One Final Commit for the Last Sprint

Home > Notes > Content

Unit Testing Rectangle Class Methods with JUnit

Notes 1

Testing Rectangle Class with JUnit

Source Code Structure

The Rectangle class under test contains dimensions, area/perimeter calculations, and a generic maximum finder with custom comparators:

import java.util.Comparator;

public class Rectangle {
    private int length;
    private int width;

    public Rectangle(int length, int width) {
        this.length = length;
        this.width = width;
    }

    public int getLength() { return length; }
    public void setLength(int length) { this.length = length; }
    public int getWidth() { return width; }
    public void setWidth(int width) { this.width = width; }

    public int computeArea() { return length * width; }
    public int computePerimeter() { return 2 * length + width; }

    public String toCoordinateString() {
        return "(" + length + "," + width + ")";
    }

    public static <T> T findMaximum(T[] array, Comparator<? super T> comparator) {
        int maxIndex = 0;
        for (int i = 1; i < array.length; i++) {
            if (comparator.compare(array[i], array[maxIndex]) > 0) {
                maxIndex = i;
            }
        }
        return array[maxIndex];
    }

    public static class AreaComparator implements Comparator<Rectangle> {
        @Override
        public int compare(Rectangle a, Rectangle b) {
            if (a.computeArea() < b.computeArea()) {
                return 1;
            } else if (a.computeArea() == b.computeArea()) {
                return 0;
            } else {
                return -1;
            }
        }
    }

    public static class PerimeterComparator implements Comparator<Rectangle> {
        @Override
        public int compare(Rectangle a, Rectangle b) {
            if (a.computePerimeter() > b.computePerimeter()) {
                return 1;
            } else if (a.computePerimeter() == b.computePerimeter()) {
                return 0;
            } else {
                return -1;
            }
        }
    }
}

Testing computeArea()

Test Design

The computeArea() method accepts two int parameters representing dimensions. Valid input are positive integers; zero and negative values are considered invalid. Invalid inputs are expected to return -1.

Test Case Input (length, width) Expected Output
TC-01 (3, 4) 12
TC-02 (-3, 4) -1
TC-03 (4, -3) -1
TC-04 (-5, -6) -1
TC-05 (3, 0) -1
TC-06 (0, 4) -1
TC-07 (0, 0) -1

Test Implementation

import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;

@RunWith(Parameterized.class)
public class AreaTest extends TestCase {
    private int dimX;
    private int dimY;
    private int expectedValue;

    public AreaTest(int dimX, int dimY, int expectedValue) {
        this.dimX = dimX;
        this.dimY = dimY;
        this.expectedValue = expectedValue;
    }

    @Parameterized.Parameters(name = "{index}: {0}*{1}={2}")
    public static Iterable<Object[]> testData() {
        return Arrays.asList(new Object[][] {
            { 3, 4, 12 },
            { -3, 4, -1 },
            { 4, -3, -1 },
            { -5, -6, -1 },
            { 3, 0, -1 },
            { 0, 4, -1 },
            { 0, 0, -1 }
        });
    }

    @Test
    public void verifyAreaCalculation() {
        assertEquals(expectedValue, new Rectangle(dimX, dimY).computeArea());
    }
}

Results

Test Case Input (length, width) Expected Actual
TC-01 (3, 4) 12 12
TC-02 (-3, 4) -1 -12
TC-03 (4, -3) -1 -12
TC-04 (-5, -6) -1 30
TC-05 (3, 0) -1 0
TC-06 (0, 4) -1 0
TC-07 (0, 0) -1 0

The implementation lacks input validation. Edge cases with zero or negative dimensions produce incorrect results.


Testing computePerimeter()

Test Design

Similar to area testing, the perimeter method requires validation for both parameters:

Test Case Input (length, width) Expected Output
TC-01 (5, 6) 22
TC-02 (-3, 4) -1
TC-03 (3, -4) -1
TC-04 (-3, -4) -1
TC-05 (3, 0) -1
TC-06 (0, 4) -1
TC-07 (0, 0) -1

Test Implemantation

import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;

@RunWith(Parameterized.class)
public class PerimeterTest extends TestCase {
    private int dimX;
    private int dimY;
    private int expectedValue;

    public PerimeterTest(int dimX, int dimY, int expectedValue) {
        this.dimX = dimX;
        this.dimY = dimY;
        this.expectedValue = expectedValue;
    }

    @Parameterized.Parameters(name = "{index}: 2*({0}+{1})={2}")
    public static Iterable<Object[]> testData() {
        return Arrays.asList(new Object[][] {
            { 5, 6, 22 },
            { -3, 4, -1 },
            { 3, -4, -1 },
            { -3, -4, -1 },
            { 3, 0, -1 },
            { 0, 4, -1 },
            { 0, 0, -1 }
        });
    }

    @Test
    public void verifyPerimeterCalculation() {
        assertEquals(expectedValue, new Rectangle(dimX, dimY).computePerimeter());
    }
}

Results

Test Case Input (length, width) Expected Actual
TC-01 (5, 6) 22 16
TC-02 (-3, 4) -1 -2
TC-03 (3, -4) -1 2
TC-04 (-3, -4) -1 -10
TC-05 (3, 0) -1 6
TC-06 (0, 4) -1 4
TC-07 (0, 0) -1 0

Two issues identified: missing input validation and incorrect formula. The implementation should use 2 * (length + width) rather than 2 * length + width.


Testing findMaximum()

Testing with AreaComparator

The generic findMaximum() method requires a comparator. The AreaComparator implementation has inverted comparison logic—returning 1 when the first area is smaller.

Test Design
Test Case Rectangle Array Expected Result
TC-01 (10,20), (2,65), (3,10), (6,20) (10,20)
TC-02 (-4,3), (5,-6), (-10,-10), (1,1) (1,1)
TC-03 (0,3), (6,0), (0,0), (1,1) (1,1)
TC-04 (0,3), (6,0), (0,0), (-1,-1) null

Note: When all rectangles in an array contain invalid dimensions, findMaximum() should return null since all computeArea() call would return -1, making all comparisons equal and selecting an invalid element.

Test Implementation
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;

@RunWith(Parameterized.class)
public class MaxAreaTest extends TestCase {
    private Rectangle[] rectArray;
    private Rectangle expectedResult;

    public MaxAreaTest(Rectangle[] rectArray, Rectangle expectedResult) {
        this.rectArray = rectArray;
        this.expectedResult = expectedResult;
    }

    @Parameterized.Parameters
    public static Iterable<Object[]> testData() {
        return Arrays.asList(new Object[][] {
            {
                new Rectangle[] {
                    new Rectangle(10, 20), new Rectangle(2, 65),
                    new Rectangle(3, 10), new Rectangle(6, 20)
                },
                new Rectangle(10, 20)
            },
            {
                new Rectangle[] {
                    new Rectangle(-4, 3), new Rectangle(5, -6),
                    new Rectangle(-10, -10), new Rectangle(1, 1)
                },
                new Rectangle(1, 1)
            },
            {
                new Rectangle[] {
                    new Rectangle(0, 3), new Rectangle(6, 0),
                    new Rectangle(0, 0), new Rectangle(1, 1)
                },
                new Rectangle(1, 1)
            },
            {
                new Rectangle[] {
                    new Rectangle(0, 3), new Rectangle(6, 0),
                    new Rectangle(0, 0), new Rectangle(-1, -1)
                },
                null
            }
        });
    }

    @Test
    public void verifyMaximumByArea() {
        Rectangle result = Rectangle.findMaximum(rectArray, new Rectangle.AreaComparator());
        String actualStr = (result != null) ? result.toCoordinateString() : "null";
        String expectedStr = (expectedResult != null) ? expectedResult.toCoordinateString() : "null";
        assertEquals(expectedStr, actualStr);
    }
}
Results
Test Case Rectangle Array Expected Actual
TC-01 (10,20), (2,65), (3,10), (6,20) (10,20) (3,10)
TC-02 (-4,3), (5,-6), (-10,-10), (1,1) (1,1) (5,-6)
TC-03 (0,3), (6,0), (0,0), (1,1) (1,1) (0,3)
TC-04 (0,3), (6,0), (0,0), (-1,-1) null (0,3)

The AreaComparator.compare() method has inverted logic—larger area should return 1, not 1 when area is smaller. Additionally, the findMaximum() method does not filter out rectangles with invalid dimensions.


Testing with PerimeterComparator

Test Design
Test Case Rectangle Array Expected Result
TC-01 (10,20), (2,65), (3,10), (6,20) (2,65)
TC-02 (-4,3), (5,-6), (-10,-10), (1,1) (1,1)
TC-03 (0,3), (6,0), (0,0), (1,1) (1,1)
TC-04 (0,3), (6,0), (0,0), (-1,-1) null
Test Implementation
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;

@RunWith(Parameterized.class)
public class MaxPerimeterTest extends TestCase {
    private Rectangle[] rectArray;
    private Rectangle expectedResult;

    public MaxPerimeterTest(Rectangle[] rectArray, Rectangle expectedResult) {
        this.rectArray = rectArray;
        this.expectedResult = expectedResult;
    }

    @Parameterized.Parameters
    public static Iterable<Object[]> testData() {
        return Arrays.asList(new Object[][] {
            {
                new Rectangle[] {
                    new Rectangle(10, 20), new Rectangle(2, 65),
                    new Rectangle(3, 10), new Rectangle(6, 20)
                },
                new Rectangle(2, 65)
            },
            {
                new Rectangle[] {
                    new Rectangle(-4, 3), new Rectangle(5, -6),
                    new Rectangle(-10, -10), new Rectangle(1, 1)
                },
                new Rectangle(1, 1)
            },
            {
                new Rectangle[] {
                    new Rectangle(0, 3), new Rectangle(6, 0),
                    new Rectangle(0, 0), new Rectangle(1, 1)
                },
                new Rectangle(1, 1)
            },
            {
                new Rectangle[] {
                    new Rectangle(0, 3), new Rectangle(6, 0),
                    new Rectangle(0, 0), new Rectangle(-1, -1)
                },
                null
            }
        });
    }

    @Test
    public void verifyMaximumByPerimeter() {
        Rectangle result = Rectangle.findMaximum(rectArray, new Rectangle.PerimeterComparator());
        String actualStr = (result != null) ? result.toCoordinateString() : "null";
        String expectedStr = (expectedResult != null) ? expectedResult.toCoordinateString() : "null";
        assertEquals(expectedStr, actualStr);
    }
}
Results
Test Case Rectangle Array Expected Actual
TC-01 (10,20), (2,65), (3,10), (6,20) (2,65) (2,65)
TC-02 (-4,3), (5,-6), (-10,-10), (1,1) (1,1) (5,-6)
TC-03 (0,3), (6,0), (0,0), (1,1) (1,1) (6,0)
TC-04 (0,3), (6,0), (0,0), (-1,-1) null (6,0)

The PerimeterComparator has correct logic, but findMaximum() still lacks validation for invalid dimensions.


Corrected Implementation

import java.util.Comparator;

public class Rectangle {
    private int length;
    private int width;

    public Rectangle(int length, int width) {
        this.length = length;
        this.width = width;
    }

    public int getLength() { return length; }
    public void setLength(int length) { this.length = length; }
    public int getWidth() { return width; }
    public void setWidth(int width) { this.width = width; }

    public int computeArea() {
        if (length > 0 && width > 0) {
            return length * width;
        }
        return -1;
    }

    public int computePerimeter() {
        if (length > 0 && width > 0) {
            return 2 * (length + width);
        }
        return -1;
    }

    public String toCoordinateString() {
        return "(" + length + "," + width + ")";
    }

    public static <T> T findMaximum(T[] array, Comparator<? super T> comparator) {
        int maxIndex = 0;
        for (int i = 1; i < array.length; i++) {
            if (comparator.compare(array[i], array[maxIndex]) > 0) {
                maxIndex = i;
            }
        }

        Rectangle candidate = (Rectangle) array[maxIndex];
        if (candidate.getLength() <= 0 || candidate.getWidth() <= 0) {
            return null;
        }
        return array[maxIndex];
    }

    public static class AreaComparator implements Comparator<Rectangle> {
        @Override
        public int compare(Rectangle a, Rectangle b) {
            if (a.computeArea() > b.computeArea()) {
                return 1;
            } else if (a.computeArea() == b.computeArea()) {
                return 0;
            } else {
                return -1;
            }
        }
    }

    public static class PerimeterComparator implements Comparator<Rectangle> {
        @Override
        public int compare(Rectangle a, Rectangle b) {
            if (a.computePerimeter() > b.computePerimeter()) {
                return 1;
            } else if (a.computePerimeter() == b.computePerimeter()) {
                return 0;
            } else {
                return -1;
            }
        }
    }

    public static void main(String[] args) {
        Rectangle[] rectArray = new Rectangle[] {
            new Rectangle(10, 20), new Rectangle(2, 65),
            new Rectangle(3, 10), new Rectangle(6, 20)
        };

        Rectangle maxByArea = findMaximum(rectArray, new AreaComparator());
        Rectangle maxByPerimeter = findMaximum(rectArray, new PerimeterComparator());

        if (maxByArea == null) {
            System.out.println("All rectangles have invalid dimensions for area comparison");
        } else {
            System.out.println("Maximum area: " + maxByArea.toCoordinateString());
        }

        if (maxByPerimeter == null) {
            System.out.println("All rectangles have invalid dimensions for perimeter comparison");
        } else {
            System.out.println("Maximum perimeter: " + maxByPerimeter.toCoordinateString());
        }
    }
}

Test Suite

Combine all test classes using JUnit Suite:

import org.junit.runner.RunWith;
import org.junit.runners.Suite;

@RunWith(Suite.class)
@Suite.SuiteClasses({
    AreaTest.class,
    PerimeterTest.class,
    MaxAreaTest.class,
    MaxPerimeterTest.class
})
public class AllTests {
}

Related Articles

Designing Alertmanager Templates for Prometheus Notifications

How to craft Alertmanager templates to format alert messages, improving clarity and presentation. Alertmanager uses Go’s text/template engine with additional helper functions. Alerting rules referenc...

Deploying a Maven Web Application to Tomcat 9 Using the Tomcat Manager

Tomcat 9 does not provide a dedicated Maven plugin. The Tomcat Manager interface, however, is backward-compatible, so the Tomcat 7 Maven Plugin can be used to deploy to Tomcat 9. This guide shows two...

Skipping Errors in MySQL Asynchronous Replication

When a replica halts because the SQL thread encounters an error, you can resume replication by skipping the problematic event(s). Two common approaches are available. Methods to Skip Errors 1) Skip a...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.