Build the Calculation Engine
By the end of this lesson, you'll be able to:
- Write a LoanCalculationService with a monthly payment formula
- Apply a credit-score-based interest rate
- Write the first formal test class for this service, immediately
Prerequisites: "Design the Approval Logic"
The calculation service
public class LoanCalculationService {
public Decimal calculateInterestRate(Decimal creditScore) {
if (creditScore >= 700) {
return 6.5;
} else if (creditScore >= 600) {
return 9.0;
} else {
return 14.0;
}
}
public Decimal calculateMonthlyPayment(Decimal amount, Decimal annualRatePercent, Integer months) {
Decimal monthlyRate = (annualRatePercent / 100) / 12;
return (amount * monthlyRate) / (1 - Math.pow(1 + monthlyRate, -months));
}
}
public class LoanCalculationService {— the class.public Decimal calculateInterestRate(Decimal creditScore) {— Module 6's if/else if/else chain.if (creditScore >= 700) {— the best tier.return 6.5;— its rate.} else if (creditScore >= 600) {— the middle tier.return 9.0;— its rate.} else {— the lowest tier.return 14.0;— its rate.}— closes theif/else if/else.}— closes the method.- (blank line)
public Decimal calculateMonthlyPayment(Decimal amount, Decimal annualRatePercent, Integer months) {— implements the standard amortized-loan-payment formula.Decimal monthlyRate = (annualRatePercent / 100) / 12;— converts the annual percentage to a monthly decimal rate.return (amount * monthlyRate) / (1 - Math.pow(1 + monthlyRate, -months));— the principal, annual rate, and loan term in months, combined into the standard formula.}— closes the method.}— closes the class.
Testing it immediately
@isTest
private class LoanCalculationServiceTest {
@isTest
static void highCreditScoreGetsLowestRate() {
LoanCalculationService service = new LoanCalculationService();
System.assertEquals(6.5, service.calculateInterestRate(750));
}
@isTest
static void midCreditScoreGetsMiddleRate() {
LoanCalculationService service = new LoanCalculationService();
System.assertEquals(9.0, service.calculateInterestRate(650));
}
@isTest
static void lowCreditScoreGetsHighestRate() {
LoanCalculationService service = new LoanCalculationService();
System.assertEquals(14.0, service.calculateInterestRate(550));
}
}
@isTest— marks the class.private class LoanCalculationServiceTest {— three test methods, one per branch of the if/else if/else chain.@isTest— marks the first test.static void highCreditScoreGetsLowestRate() {— this is Module 31's Lesson 2 habit ("one test method, one scenario") applied the moment the logic is written.LoanCalculationService service = new LoanCalculationService();— creates the service.System.assertEquals(6.5, service.calculateInterestRate(750));— verifies the top tier.}— closes this test.- (blank line)
@isTest— marks the second test.static void midCreditScoreGetsMiddleRate() {— a different scenario.LoanCalculationService service = new LoanCalculationService();— creates the service.System.assertEquals(9.0, service.calculateInterestRate(650));— verifies the middle tier.}— closes this test.- (blank line)
@isTest— marks the third test.static void lowCreditScoreGetsHighestRate() {— another scenario.LoanCalculationService service = new LoanCalculationService();— creates the service.System.assertEquals(14.0, service.calculateInterestRate(550));— verifies the lowest tier.}— closes this test.}— closes the class.
Testing the boundary values specifically
@isTest
static void creditScoreOf700IsTheLowestQualifyingForBestRate() {
LoanCalculationService service = new LoanCalculationService();
System.assertEquals(6.5, service.calculateInterestRate(700));
}
@isTest
static void creditScoreOf699JustMissesTheBestRate() {
LoanCalculationService service = new LoanCalculationService();
System.assertEquals(9.0, service.calculateInterestRate(699));
}
@isTest— marks the first test.static void creditScoreOf700IsTheLowestQualifyingForBestRate() {— testing exactly700— the boundary between two branches.LoanCalculationService service = new LoanCalculationService();— creates the service.System.assertEquals(6.5, service.calculateInterestRate(700));—700should qualify for the best rate.}— closes this test.- (blank line)
@isTest— marks the second test.static void creditScoreOf699JustMissesTheBestRate() {— the value one below the boundary.LoanCalculationService service = new LoanCalculationService();— creates the service.System.assertEquals(9.0, service.calculateInterestRate(699));—699should just miss it — this is where off-by-one bugs (>instead of>=, from Module 4) actually hide.}— closes this test.
This is Module 31's "assertions that actually tell you something" lesson, applied specifically to boundary conditions.
Exercise
Write a test confirming calculateInterestRate(600) returns 9.0 (the boundary between the middle and lowest tiers).
Show hint
Test the exact boundary value, not just a value comfortably inside the range.
Build the Calculation Engine Quiz
My Notes
Log in to keep private notes on this lesson.
Questions about this lesson
No questions yet — be the first to ask.
Log in to ask a question about this lesson.
Summary
This lesson builds the calculation logic and its test class together — the standard shape of every lesson going forward in this module.