patternjavaMinor
Calculate loan rate based on payment and duration in months
Viewed 0 times
paymentratecalculatebaseddurationandloanmonths
Problem
So I have the following code which works well-ish. Looking for ideas on how to improve it.
```
public void testItAll() {
System.err.println(determineRate(226.34, 25.00, 12));
System.err.println(determineRate(5800.00, 29.00, 31));
System.err.println(determineRate(4000.00, 25.00, 460));
System.err.println(determineRate(4000.00, 111.00, 173));
System.err.println(determineRate(15000.00, 270.00, 60));
System.err.println(determineRate(7000.00, 154.00, 60));
System.err.println(determineRate(27002.51, 315.00, 85));
System.err.println(determineRate(17118.33, 270.14, 73));
System.err.println(determineRate(5170.71, 143.00, 48));
System.err.println(determineRate(45297.34, 400.00, 135));
System.err.println(determineRate(6000.00, 113.00, 60));
System.err.println(determineRate(23058.98, 219.59, 145));
System.err.println(determineRate(47475.76, 390.44, 181));
System.err.println(determineRate(69691.48, 554.00, 180));
System.err.println(determineRate(39310.00, 725.00, 60));
System.err.println(determineRate(45000.00, 316.00, 180));
System.err.println(determineRate(14071.01, 45.00, 220));
System.err.println(determineRate(16875.00, 198.00, 120));
System.err.println(determineRate(66080.04, 295.00, 12));
System.err.println(determineRate(120000.00, 664.00, 120));
System.err.println(determineRate(58000.00, 387.19, 120));
System.err.println(determineRate(351213.00, 1993.11, 25));
System.err.println(determineRate(139500.00, 1000.00, 300));
System.err.println(determineRate(2400.00, 1875.00, 180));
System.err.println(determineRate(193155.00, 2002.00, 120));
System.err.println(determineRate(40800.00, 507.46, 36));
System.err.println(determineRate(198375.00, 1530.00, 240));
System.err.println(determineRate(22700.00, 450.00, 60));
System.err.println(determineRate(20000.00, 622.85, 999));
System.err.println(determineRate(629999.32, 25.00, 300));
System.err.println(determineR
```
public void testItAll() {
System.err.println(determineRate(226.34, 25.00, 12));
System.err.println(determineRate(5800.00, 29.00, 31));
System.err.println(determineRate(4000.00, 25.00, 460));
System.err.println(determineRate(4000.00, 111.00, 173));
System.err.println(determineRate(15000.00, 270.00, 60));
System.err.println(determineRate(7000.00, 154.00, 60));
System.err.println(determineRate(27002.51, 315.00, 85));
System.err.println(determineRate(17118.33, 270.14, 73));
System.err.println(determineRate(5170.71, 143.00, 48));
System.err.println(determineRate(45297.34, 400.00, 135));
System.err.println(determineRate(6000.00, 113.00, 60));
System.err.println(determineRate(23058.98, 219.59, 145));
System.err.println(determineRate(47475.76, 390.44, 181));
System.err.println(determineRate(69691.48, 554.00, 180));
System.err.println(determineRate(39310.00, 725.00, 60));
System.err.println(determineRate(45000.00, 316.00, 180));
System.err.println(determineRate(14071.01, 45.00, 220));
System.err.println(determineRate(16875.00, 198.00, 120));
System.err.println(determineRate(66080.04, 295.00, 12));
System.err.println(determineRate(120000.00, 664.00, 120));
System.err.println(determineRate(58000.00, 387.19, 120));
System.err.println(determineRate(351213.00, 1993.11, 25));
System.err.println(determineRate(139500.00, 1000.00, 300));
System.err.println(determineRate(2400.00, 1875.00, 180));
System.err.println(determineRate(193155.00, 2002.00, 120));
System.err.println(determineRate(40800.00, 507.46, 36));
System.err.println(determineRate(198375.00, 1530.00, 240));
System.err.println(determineRate(22700.00, 450.00, 60));
System.err.println(determineRate(20000.00, 622.85, 999));
System.err.println(determineRate(629999.32, 25.00, 300));
System.err.println(determineR
Solution
Unit testing
Kind of a good start...
Eh, what? One, why are you printing out on the standard error stream? Two, how do you complete the so-called 'testing'? By manually checking the output matches?
This is where unit testing comes in! A proper unit test should let a developer:
There are a handful of good Java unit testing frameworks, and I will use TestNG as an example below.
You start off by having annotating a test method so that the framework recognizes that it needs to run that test (this is dependent on your unit testing framework), and an underlying method that calls your method to test:
As you can see, its method signature is very similar to your method, because you need the desired inputs. The extra parameter,
Testing a number of inputs and expected results is relatively easy with TestNG's parameterized testing feature. You need a
When you run this unit test, TestNG will iterate through all the test cases to assert the results. This eliminates the need to manually check every calculation.
Magic numbers and math
Using just one example,
Another way of expressing this can be just:
public void testItAll() { ... }Kind of a good start...
System.err.println(determineRate(226.34, 25.00, 12));Eh, what? One, why are you printing out on the standard error stream? Two, how do you complete the so-called 'testing'? By manually checking the output matches?
This is where unit testing comes in! A proper unit test should let a developer:
- Specify some test input.
- Process the input to some output to be recorded.
- Assert that the recorded output matches an expected result.
There are a handful of good Java unit testing frameworks, and I will use TestNG as an example below.
You start off by having annotating a test method so that the framework recognizes that it needs to run that test (this is dependent on your unit testing framework), and an underlying method that calls your method to test:
@Test
public void testOne() {
doTest(226.34, 25.00, 12, 55.50992);
}
public void testLoanRate(double loanAmount, double payment, int termInMonths,
double expectedRate) {
// ...
}As you can see, its method signature is very similar to your method, because you need the desired inputs. The extra parameter,
expectedRate, lets you assert that the calculation is correct. The body of the method can be something like:public void testLoanRate(double loanAmount, double payment, int termInMonths,
double expectedRate) {
// assuming determineRate() can be made static
double result = determineRate(loanAmount, payment, termInMonths);
// following static method is provided by TestNG
// the third argument controls the absolute tolerable difference,
// an arbitrary small amount is chosen as an example
Assert.assertEquals(result, expectedRate, 0.0001d);
// optionally print inputs and output when successful
System.out.printf("Amount: %f, Payment: %f, Term in months: %d, Result: %f%n",
loanAmount, payment, termInMonths, result);
}Testing a number of inputs and expected results is relatively easy with TestNG's parameterized testing feature. You need a
@DataProvider, and link it up with the @Test method on the testLoanRate() method now:@DataProvider(name = "test-cases")
public Iterator getTestCases() {
return Arrays.asList(
new Object[] { 226.34, 25.00, 12, 55.50992 },
new Object[] { 5800.00, 29.00, 31, 0 }, // ???
new Object[] { 4000.00, 25.00, 460, 6.97672 },
/* ... */
).iterator();
}
@Test(dataProvider = "test-cases")
public void testLoanRate(double loanAmount, double payment, int termInMonths,
double expectedRate) {
double result = determineRate(loanAmount, payment, termInMonths);
Assert.assertEquals(result, expectedRate, 0.0001d);
System.out.printf("Amount: %f, Payment: %f, Term in months: %d, Result: %f%n",
loanAmount, payment, termInMonths, result);
}When you run this unit test, TestNG will iterate through all the test cases to assert the results. This eliminates the need to manually check every calculation.
Magic numbers and math
private double calculatePayment(double loanAmount, double rate, double termInMonths) {
return (loanAmount * ((rate/12.0)*(Math.pow((1 + (rate/12.0)), termInMonths))))
/ (Math.pow((1 + (rate/12.0)), termInMonths) - 1);
}Using just one example,
12.0 seems to be used a lot here. You should consider putting your magic numbers into an easily identifiable constant so that they can be reused in a consistent and readable manner.Math.max(payment, calculatedPayment) - Math.min(payment, calculatedPayment) > 0.05Another way of expressing this can be just:
Math.abs(payment - calculatedPayment) > 0.05Code Snippets
public void testItAll() { ... }System.err.println(determineRate(226.34, 25.00, 12));@Test
public void testOne() {
doTest(226.34, 25.00, 12, 55.50992);
}
public void testLoanRate(double loanAmount, double payment, int termInMonths,
double expectedRate) {
// ...
}public void testLoanRate(double loanAmount, double payment, int termInMonths,
double expectedRate) {
// assuming determineRate() can be made static
double result = determineRate(loanAmount, payment, termInMonths);
// following static method is provided by TestNG
// the third argument controls the absolute tolerable difference,
// an arbitrary small amount is chosen as an example
Assert.assertEquals(result, expectedRate, 0.0001d);
// optionally print inputs and output when successful
System.out.printf("Amount: %f, Payment: %f, Term in months: %d, Result: %f%n",
loanAmount, payment, termInMonths, result);
}@DataProvider(name = "test-cases")
public Iterator<Object[]> getTestCases() {
return Arrays.asList(
new Object[] { 226.34, 25.00, 12, 55.50992 },
new Object[] { 5800.00, 29.00, 31, 0 }, // ???
new Object[] { 4000.00, 25.00, 460, 6.97672 },
/* ... */
).iterator();
}
@Test(dataProvider = "test-cases")
public void testLoanRate(double loanAmount, double payment, int termInMonths,
double expectedRate) {
double result = determineRate(loanAmount, payment, termInMonths);
Assert.assertEquals(result, expectedRate, 0.0001d);
System.out.printf("Amount: %f, Payment: %f, Term in months: %d, Result: %f%n",
loanAmount, payment, termInMonths, result);
}Context
StackExchange Code Review Q#132882, answer score: 3
Revisions (0)
No revisions yet.