Kotest has shouldBeSorted plus matchers such as shouldBeSortedWith, shouldBeMonotonicallyIncreasing and shouldBeStrictlyIncreasing. What does each assert, and what are the traps in asserting sortedness?
answer
- Sorted = natural ascending, ties OK
- SortedWith = comparator (compareBy/thenBy)
- Strict vs monotonic = ties forbidden vs allowed
- Empty/singleton pass vacuously — assert size
- Sortedness does not pin contents
basics
~20 sshouldBeSorted checks natural ascending order on Comparable elements and allows equal neighbours. shouldBeSortedWith takes a custom comparator. Monotonically increasing allows ties; strictly increasing forbids them. Traps: empty and single-element collections pass trivially, and sortedness alone does not pin the contents.
solid answer
~50 sThe family splits along two axes — which ordering, and whether ties are allowed. - `shouldBeSorted()` — elements must be in ascending natural order, so the element type must be `Comparable`. Equal adjacent elements are fine. - `shouldBeSortedWith(comparator)` — same assertion under a supplied `Comparator` (or comparison lambda). This is what you use for domain ordering such as "by due date, then priority", or for descending order. - `shouldBeMonotonicallyIncreasing()` / `shouldBeMonotonicallyDecreasing()` — non-decreasing / non-increasing: ties allowed. - `shouldBeStrictlyIncreasing()` / `shouldBeStrictlyDecreasing()` — ties rejected, which is how you assert uniqueness *and* order in one matcher (useful for sequence numbers or timestamps that must advance). Two traps. First, an empty or single-element collection satisfies every one of these vacuously, so pair a sortedness assertion with a size assertion. Second, sortedness under-specifies: it says nothing about which elements are present. If the contract is "these three rows, sorted", `shouldContainExactly` on the expected ordered list asserts both properties at once.
code
kotlin · 7 lineslistOf(1, 2, 2, 3).shouldBeSorted() // ties allowed
listOf(1, 2, 2, 3).shouldBeStrictlyIncreasing() // fails on the tie
tasks.shouldBeSortedWith(compareBy<Task> { it.dueDate }.thenBy { it.priority })
results shouldHaveSize 3 // guards vacuous pass
results.shouldBeSortedWith(compareByDescending { it.score })go deeper
Know that shouldBeSorted checks ascending natural order and that a comparator variant exists for custom ordering.
Distinguish strict from monotonic on ties, use compareBy/thenBy with shouldBeSortedWith, and mention the vacuous-pass trap.
Lead with the two traps — vacuous truth and under-specification — and show a layered assertion set that pins size, order and uniqueness separately.
Frame ordering as part of the API contract: decide where ordering is guaranteed, express it once, and keep tests from inventing ordering guarantees the system does not make.
## What the matchers assert Kotest's ordering matchers all work on collections of `Comparable` elements (or with an explicit comparator) and check the relationship between adjacent elements. ```kotlin listOf(1, 2, 2, 3).shouldBeSorted() // passes: ties allowed listOf(1, 2, 2, 3).shouldBeMonotonicallyIncreasing() // passes listOf(1, 2, 2, 3).shouldBeStrictlyIncreasing() // FAILS: 2 == 2 listOf(3, 2, 1).shouldBeMonotonicallyDecreasing() // passes listOf(3, 2, 1).shouldBeSortedWith(compareByDescending { it }) // passes ``` **Natural versus custom ordering.** `shouldBeSorted()` uses the element type's own `compareTo`. As soon as the ordering is domain-specific — descending, by a property, multi-key — you need `shouldBeSortedWith`, which accepts a `Comparator`. Kotlin's `compareBy`, `compareByDescending` and `thenBy` build those readably: ```kotlin tasks.shouldBeSortedWith(compareBy<Task> { it.dueDate }.thenBy { it.priority }) ``` **Ties.** The monotonic/strict pair exists precisely to make the tie question explicit. "Monotonically increasing" means never decreasing — `[1, 1, 2]` is fine. "Strictly increasing" means every step goes up — `[1, 1, 2]` fails. Choosing the strict variant is how you assert "ordered *and* no duplicates" in a single matcher, which is exactly right for generated sequence numbers, version counters, or event timestamps that must advance. ## Trap 1: vacuous truth Every ordering matcher is a statement about *adjacent pairs*. An empty collection has none; a single-element collection has none. So: ```kotlin emptyList<Int>().shouldBeSorted() // passes listOf(42).shouldBeStrictlyIncreasing() // passes ``` A test whose query silently returned nothing will happily go green. Always pair the ordering assertion with a size or non-empty assertion: ```kotlin results shouldHaveSize 3 results.shouldBeSortedWith(compareBy { it.createdAt }) ``` This is the single most valuable point to make in an interview about this family, because it is a real way tests silently stop testing. ## Trap 2: sortedness under-specifies Ordering matchers say nothing about *which* elements are present. `[1, 5, 9]` and `[2, 3, 4]` are both sorted. If your test knows the expected contents, asserting the expected list with `shouldContainExactly` covers both contents and order in one assertion and produces a better failure report. Ordering matchers earn their keep when the contents are genuinely not predictable — results from a real database, generated data, a property-based test — but the *ordering* is contractual. That is the honest use case: "whatever came back, it must be ordered by score descending". ## Trap 3: comparator/equality mismatch A comparator inconsistent with `equals`, or one that orders on a nullable or non-total key, produces confusing outcomes. Sorting by a property whose values collide means "sorted" is satisfied by several different arrangements, so an ordering assertion may pass for output your users would call wrong. If tie-breaking matters to the contract, put the tie-breaker in the comparator (`thenBy`) so the assertion actually pins it. ## Trap 4: Comparable requirement `shouldBeSorted()` needs a `Comparable` element type. For domain objects that are not comparable — and most should not be — the compiler pushes you to `shouldBeSortedWith`, which is the right outcome: the ordering belongs in the test's comparator, not bolted onto the domain type just to make an assertion compile. Adding `Comparable` to a domain class purely for testing is a smell worth calling out. ## Putting it together ```kotlin val page = repository.topScores(limit = 10) page shouldHaveSize 10 // not vacuous page.shouldBeSortedWith(compareByDescending { it.score }) // the contract page.map { it.userId }.shouldNotContainDuplicates() // no user twice ``` Three assertions, each pinning a distinct property of the contract, none of them redundant. ## Interview-ready summary Natural order versus comparator; ties allowed (monotonic/sorted) versus forbidden (strict). Empty and single-element collections pass everything vacuously, so assert size too. And prefer `shouldContainExactly` on an expected ordered list whenever the contents are known — reserve ordering matchers for the case where only the ordering, not the content, is contractual.
- Why can an ordering assertion pass on a broken query?Because ordering matchers only constrain adjacent pairs. An empty result has no pairs, so it satisfies every one of them vacuously, and a single-element result does too. A query that silently returns nothing therefore goes green. Pairing the assertion with `shouldHaveSize` or a non-empty check closes that hole.
- When is asserting sortedness better than asserting the expected list with shouldContainExactly?When the contents genuinely are not predictable — results from a live database, randomly generated data, a property-based test — but the ordering is part of the contract. If the test knows exactly which elements should come back, `shouldContainExactly` on the expected ordered list asserts contents and order together and reports failures better.
saying these in an interview costs you the question
- Assuming shouldBeSorted rejects equal adjacent elements
- Not realising empty and single-element collections satisfy every ordering matcher
- Treating a passing sortedness assertion as proof the right elements were returned
- Making a domain class Comparable purely so shouldBeSorted compiles
- Asserting sortedness on a key with ties and expecting a specific arrangement