Advanced 35 min read

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));
    }
}

calculateInterestRate is Module 6's if/else if/else chain; calculateMonthlyPayment implements the standard amortized-loan-payment formula, taking the principal, annual rate, and loan term in months.

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));
    }
}

Three test methods, one per branch of the if/else if/else chain — this is Module 31's Lesson 2 habit ("one test method, one scenario") applied the moment the logic is written, not saved for a separate lesson afterward.

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));
}

Testing exactly 700 and 699 — the boundary between two branches — is where off-by-one bugs (> instead of >=, from Module 4) actually hide. 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.

APEX

Build the Calculation Engine Quiz

1. Why does this lesson specifically test the boundary values 699 and 700?

Log in to submit the quiz and save your score.

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.