Mutation testing involves making small changes to code to test whether tests can detect those changes. It is more effective than just measuring test coverage at finding flaws in tests. Tools like Pitest and Descartes can perform mutation testing. Mutations may change conditionals, remove lines, or replace method bodies. Mutation testing is slower but can find cases where tests rely on implementation details. It is best run locally during development and as part of PRs, potentially failing PRs with too low mutation scores. This helps improve test quality over time.
4. ThoughtWorks
Our teams have used Pitest for a while now, and we
recommend its use in Java projects to measure the
health of the test suite.
Technology Radar, Oct 2020
14. Run steps
1. Collect coverage
2. Determine what to mutate and how
3. Execute each mutation
a. load modified class → execute covering tests
b. killed mutation = some test failed; otherwise survived
15. Testing approach vs. mutation testing
➔ test 1:1 code, isolated ☢👍
➔ test high-level API, use DB ☢👎
34. Gregor, mutators=ALL
fun sendNotifications() {
invoiceRepository.findAllByStatusNotIn(listOf(PAID))
.filter { isOverdue(it, clock) }
.next()
.doOnNext { sendNotification(it) }
.subscribe()
}
private fun sendNotification(invoice: Invoice) {
notificationSender.sendNotification("${invoice.status} is overdue!")
}
⬅
35. Blind spots
➔ bad values in a constant list
◆ listOf(PAID, CANCELLED)
➔ bad field or constant
◆ dto.timeFinished = entity.timeStarted
➔ fluent APIs (reactive programming)
◆ .next()
44. When to run
➔ locally during development slow
➔ report in PR
45. When to run
➔ locally during development slow
➔ report in PR false positives and blind spots
➔ fail PR if too low
46. When to run
➔ locally during development slow
➔ report in PR false positives and blind spots
➔ fail PR if too low wasting effort
➔ very low test quality?