KOTLIN / CHAPTER 13
Validation, failures, and contract tests
AdvancedReject unsupported input without corrupting state.
By the end of this chapter
- Choose validation boundaries
- Test empty, tied, and invalid cases
- Preserve distinctions between rejection and valid zero
The idea
A function should state which inputs it accepts and what happens outside that domain. require is useful for argument preconditions and throws IllegalArgumentException when its condition fails. For expected user mistakes, an explicit nullable or sealed result may be a clearer contract. Neither approach should silently turn invalid input into a plausible successful value that callers cannot distinguish from real data.
Validate before applying a state change when failure must leave the original unchanged. Tests should exercise the boundary itself, not only obviously valid and invalid extremes. For a limit of ten, test nine, ten, and eleven. Add empty and duplicate inputs when collections are involved, and verify tie behavior independently from maximum values. Derive expected results from the specification rather than recreating the implementation inside the test.
fun checkedCount(text:String):Int? {val n=text.toIntOrNull()?:return null;return n.takeIf{it>=0}}
fun main(){check(checkedCount("0")==0);check(checkedCount("-1")==null);check(checkedCount("x")==null);println("checks passed")}checks passed
Zero remains a successful count, while malformed and negative inputs are both rejected explicitly.
Try it yourself
Test a bounded transfer for exact-capacity success and failed-update state preservation.
Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.
CHECK YOUR UNDERSTANDING