KOTLIN / CHAPTER 13

Validation, failures, and contract tests

Advanced

Reject 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.

Kotlin example
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")}
Result
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

Chapter checkpoint

Which pair targets an inclusive upper boundary of ten most directly?