Advanced
30 min read
Enforce Validation Rules
By the end of this lesson, you'll be able to:
- Validate that a loan amount and income are both positive
- Validate that a credit score falls within a realistic range
- Write tests confirming both valid and invalid input are handled correctly
Prerequisites: "Build the Calculation Engine"
Validating the inputs
public class LoanValidationException extends Exception {}
public class LoanValidationService {
public void validate(Decimal requestedAmount, Decimal annualIncome, Decimal creditScore) {
if (requestedAmount == null || requestedAmount <= 0) {
throw new LoanValidationException('Requested amount must be positive.');
}
if (annualIncome == null || annualIncome <= 0) {
throw new LoanValidationException('Annual income must be positive.');
}
if (creditScore == null || creditScore < 300 || creditScore > 850) {
throw new LoanValidationException('Credit score must be between 300 and 850.');
}
}
}
public class LoanValidationException extends Exception {}— exactly Module 22'sLibraryExceptionand Module 23'sInventoryExceptionpattern — one dedicated exception type.- (blank line)
public class LoanValidationService {— the validation service.public void validate(Decimal requestedAmount, Decimal annualIncome, Decimal creditScore) {— the method.if (requestedAmount == null || requestedAmount <= 0) {— the first rule, stated as a clear, individually-testable condition.throw new LoanValidationException('Requested amount must be positive.');— rejects with a specific message.}— closes theif.if (annualIncome == null || annualIncome <= 0) {— the second rule.throw new LoanValidationException('Annual income must be positive.');— rejects with its own message.}— closes theif.if (creditScore == null || creditScore < 300 || creditScore > 850) {— the third rule.throw new LoanValidationException('Credit score must be between 300 and 850.');— rejects with its own message.}— closes theif.}— closes the method.}— closes the class.
Testing the rejection paths
@isTest
private class LoanValidationServiceTest {
@isTest
static void negativeAmountThrows() {
LoanValidationService validator = new LoanValidationService();
try {
validator.validate(-1000, 50000, 700);
System.assert(false, 'Expected a LoanValidationException');
} catch (LoanValidationException e) {
System.assert(e.getMessage().contains('amount'));
}
}
@isTest
static void creditScoreAbove850Throws() {
LoanValidationService validator = new LoanValidationService();
try {
validator.validate(10000, 50000, 900);
System.assert(false, 'Expected a LoanValidationException');
} catch (LoanValidationException e) {
System.assert(e.getMessage().contains('Credit score'));
}
}
}
@isTest— marks the class.private class LoanValidationServiceTest {— the class.@isTest— marks the first test.static void negativeAmountThrows() {— this is Module 31's "Testing Exceptions" lesson, applied to real project code the moment it's written.LoanValidationService validator = new LoanValidationService();— creates the validator.try {— wraps the call expected to throw.validator.validate(-1000, 50000, 700);— an invalid, negative amount.System.assert(false, 'Expected a LoanValidationException');— reached only if it unexpectedly didn't throw.} catch (LoanValidationException e) {— catches the expected failure.System.assert(e.getMessage().contains('amount'));— verifies it's the right error.}— closes thetry/catch.}— closes this test.- (blank line)
@isTest— marks the second test.static void creditScoreAbove850Throws() {— a different invalid-input scenario.LoanValidationService validator = new LoanValidationService();— creates the validator.try {— wraps the call.validator.validate(10000, 50000, 900);— an out-of-range credit score.System.assert(false, 'Expected a LoanValidationException');— reached only if it didn't throw.} catch (LoanValidationException e) {— catches it.System.assert(e.getMessage().contains('Credit score'));— verifies the right error.}— closes thetry/catch.}— closes this test.}— closes the class.
Testing the acceptance path too
@isTest
static void validInputsDoNotThrow() {
LoanValidationService validator = new LoanValidationService();
validator.validate(10000, 50000, 700); // should complete without throwing
System.assert(true); // reaching this line confirms no exception was thrown
}
@isTest— marks the test method.static void validInputsDoNotThrow() {— exactly Module 31's "confirm it does NOT throw" habit — a valid set of inputs deserves its own explicit test.LoanValidationService validator = new LoanValidationService();— creates the validator.validator.validate(10000, 50000, 700);— should complete without throwing.System.assert(true);— reaching this line confirms no exception was thrown, rather than just assuming "the rejection tests passing" implies the acceptance path works too.}— closes the test method.
Exercise
Write a test confirming that a null annualIncome throws a LoanValidationException.
Show hint
Use the try/System.assert(false)/catch pattern.
Enforce Validation Rules 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
Before the approval decision itself, this lesson validates the raw inputs — and, following this module's pattern, tests both the rejection and acceptance paths immediately.