Assignment
In this Application, you are going to write an API for a Fractions class. It will accept fractions as input and perform various mathematical operations on them. Include screenshots of the application running. Since this application is an API and not a complete program,include screenshots of your usage of JUnit.
Programming
NetBeans organizes Java programs as Projects. These projects are where you'll write your program, and they contain all the classes, library references, and code necessary to run the program. Create a new project called Fractions. Within Fractions, create a new class called Fraction. NetBeans will supply a main method; delete this method, as you will be writing an API, not a complete program.
Before performing any operations on two fractions, you should first put them in lowest terms. For this you will also need to compute the greatest common divisor (GCD) of the numerator and denominator and then divide both by the GCD. You can compute the GCD of two numbers by using Euclid's Algorithm. If you're not familiar with this algorithm, there are a number of good definitions available on the web. (You'll find that searching the web for definitions, code snippets, and best practices is an invaluable tool for any programmer.) A Java implementation of Euclid's Algorithm is included as the gcd method below.
In your Fraction class, define two private int fields; these will hold the numerator and denominator of a fraction.
Define four static methods, named add, subtract, multiply, and divide. Each of these methods should be defined to take two Fraction arguments and return a new Fraction. For now, though, each of these methods should return null.
Define a constructor that takes twoint parameters, corresponding to the numerator and denominator of a fraction. For now, the constructor should do nothing with these parameters; the constructor body should be empty. You may think this odd, but the reasons will be explained shortly.
Define one more static method to compute the greatest common divisor and name it gcd. The method should take twoint parameters and return an int result. A Java implementation of gcd that uses Euclid's Algorithm is provided when you get to the testing portion of this assignment.
Finally, define an equals method, with exactly this signature:
Override publicboolean equals(Object o) { ... }
It should initially return false.
Add "doc" comments to each of the methods above. As you may recall, a documentation (doc) comment is a comment placed immediately before a class or method, to provide information to a programmer who wants to use these methods. (Other kinds of documentation, such as how to use the program or the details of how the method does its job, do not belong here.) In NetBeans, choose Tools → Analyze Javadoc, check all relevant checkboxes, and click Fix Selected; this will write skeleton doc comments for you, but you still have to fill in words to tell what each method does, what parameters it expects, and what it returns.
The JUnit Testing Framework Except for the fully-coded gcd method, the methods you've just defined are essentially "stubs"; they provide a framework for the method and show what the input and output will be, but they don't perform the actual computations. At this point, it makes sense to provide a brief explanation of why you should first write method "stubs" that do nothing, or worse, return the wrong answer. It is a generally accepted truism that you should decide what a method is supposed to do before you write the method. When you have a plan, it is much easier to figure out where you are going or what you need to do. Many developers find it effective to write the tests for the methods before they write the actual code. This approach, called Test-Driven Development (TDD), turns out to have a number of benefits, including the following:
- TDD encourages the user of smaller, easily tested methods
- Errors are caught sooner and are more easily localized
- Long and painful debugging sessions are largely eliminated
- The tests actually get written, and are available for future use
Unit is a testing framework that provides you with the ability to perform Test-Driven Development within NetBeans. With JUnit, you can have NetBeans create your test methods for you. Since NetBeans doesn't know what your methods are supposed to do, the test methods it creates will also be stubs; you need to fill in the actual test code.
To install the JUnit plug-in, select your Fraction class in NetBeans, and then go to File > New File... > JUnit > Test for Existing Class. Click Next>, and then enter the name fraction.Fraction in the Class Name: box. Click Finish, choose JUnit 4.x, and then click Select. If you have done this correctly, you will see that NetBeans has written a FractionTest class for you, with several test method stubs.
Run the tests by choosing Run > Test Project (Fraction). You will get an easy-to-read display showing that all your tests failed, as they should have since you haven't written the code yet. Notice that double-clicking on an error message will move the cursor to the failed test.
Since the TDD process requires you to navigate frequently between the code and testing, you may want to adjust NetBeans to show them side by side. To see both Fraction and FractionTestat the same time, click and hold on either of those tabs and drag it to the right or left side of the edit window.
You will find it helpful to create a version of toString for the Fraction class. This will allow failed JUnit tests to print your fractions in a readable format. Here is an example:
public String toString() {
return numerator + "/" + denominator;
}
Creating and testing the methods in the Fraction Class
Usually it's best to work on one method at a time, starting with the methods that don't depend on other methods. Sometimes that's a bit tricky. In this case, if you don't have a proper constructor, you have no fractions to test; and if you don't have a working equals method, you can't test that your results are correct. As a result, you should start by going back and forth between the two.
To develop each method, follow this procedure:
1. Fill in the test stub for the method with some actual tests.
2. Run the tests and make sure your new test code fails.
3. Fill in the method stub with actual code to do the work.
4. Run the tests and make sure the new test passes (if not, debug and repeat).
5. If the method should do more, return to step 1 to add more tests.
The following procedure steps you through this test-code-test-code sequence for the equalsmethod. Afterward, you'll use this same sequence for developing the other methods.
For the equals method, three cases need to be tested.
- Case 1 is when two fractions are identical and the equals method should say they are equal.
- Case 2 is when two fractions are completely different and the equals method should say they are unequal.
- Case 3 is when two fractions are equal but one or both aren't in lowest terms and appear to be different at first glance.
Case 1
In the testEquals method, replace the body (all eight lines) with the following:
assertEquals(new Fraction(1, 2), new Fraction(1, 2));
Now run the tests. All of them should fail, but for now just pay attention to testEquals. It should fail, because you defined an equals method that always returns false.
Next, fill in code for the equals test. It should cast the argument to a Fraction; then test that the two numerators are equal and the two
denominators are equal. Run the tests again. This time, testFraction should pass. (If it doesn't, debug your code and try again.)
Case 2
Add the following line to testEquals:
assertFalse(new Fraction(1, 2).equals(new Fraction(1, 4)));
Run the test again. It does not pass because the default (empty) constructor just sets both the numerator and the denominator to zero. Fix the constructor to save its parameters, and run the tests again; this time testEquals should pass.
Case 3
Add the following line to testEquals:
assertEquals(new Fraction(1, 2), new Fraction(2, 4));
This really should pass because 1/2 = 2/4, but it doesn't. The best way to fix this is to keep all fractions in lowest terms, so that when you call new Fraction(2, 4), it is really saved as 1/2, not as 2/4.
To reduce a fraction to the lowest terms, divide both the numerator and the denominator by their greatest common divisor. Use the gcd method above for testing.
In the testGcd method, add one or more statements such as:
assertEquals(5, Fraction.gcd(15, 25));
Now use this Java implementation of the gcd method in your Fraction constructor:
staticintgcd(int a, int b) {
a = Math.abs(a);
b = Math.abs(b);
while (a != b) {
if (a > b) a = a - b;
else b = b - a;
}
return a;
}
Although the gcd method has been provided for you, writing tests will help ensure that you understand what the method does.
Follow the test-code-test-code sequence for the add, subtract, multiply, and divide methods. Take a look at the formulae above for help implementing these methods.
You should also think about whether your code works for negative fractions.
As you can see, the TDD approach involves a lot of going back and forth between the test cases and the code being tested, never spending more than a few minutes in each. This may seem strange at first, but you will find that TDD practically guarantees you will make steady progress, finish your programs, and get them largely correct, because you will rarely if ever have to look through large amounts of code to find a bug.