A complete Java implementation of the card game Exploding Kittens, built as a software-quality engineering exercise: 73 test classes against 63 source classes, with mutation testing, static analysis, and style enforcement all gated by CI.
The game itself is the easy part. The point of this project was to find out what it takes for a test suite to actually be trustworthy — not just green.
A passing test suite proves very little. A test that asserts the wrong thing passes just as happily as one that asserts the right thing. So this project layers four independent checks, each of which catches something the others miss:
| Layer | Tool | What it catches |
|---|---|---|
| Unit tests | JUnit 5, Mockito, EasyMock | Broken logic in a single class |
| Acceptance tests | Cucumber (Gherkin) | A rule of the game implemented correctly but wired up wrong |
| Coverage | JaCoCo | Code that no test ever executes |
| Mutation testing | PIT | Code that is executed but never actually asserted on |
That last row is the one that changes how you write tests. PIT deliberately injects faults
into the compiled bytecode — flipping a conditional, changing + to -, replacing a return
value — and then reruns the suite. If the tests still pass, the mutant "survived", and that
line of code is not really tested even though coverage reports it as covered.
Configured mutation operators:
mutators.set(listOf(
"CONDITIONALS_BOUNDARY", // < becomes <="INCREMENTS", // ++ becomes --"INVERT_NEGS", // -x becomes x"MATH", // + becomes -"NEGATE_CONDITIONALS", // == becomes !="PRIMITIVE_RETURNS", // return x becomes return 0"VOID_METHOD_CALLS"// a call is removed entirely
))Writing tests that kill these mutants is a different discipline from writing tests that
reach a coverage number. CONDITIONALS_BOUNDARY in particular forces you to test the exact
edges — a deck with one card left, a turn counter at its limit — rather than only the
comfortable middle of each range.
Model–view–controller, with each layer independently testable:
src/main/java/explodingkittens/
├── model/ 31 classes cards, deck, players, turn state
├── exceptions/ 10 classes one type per rule violation
├── controller/ 9 classes game loop, setup, per-card effects
├── view/ 6 classes console rendering and input
├── service/ 4 classes deck construction, shuffling, dealing
└── util/ 2 classes
Two decisions that made the suite possible:
- Input handling is an interface, not a call to
Scanner. Every card that needs a player decision (Favor, Cat Card steal, Defuse placement) takes an input handler, so the controller can be driven by a mock in tests and by the console at runtime. - One exception type per rule violation (
EmptyDeckException,EmptyPlayersListException, and eight more) rather than a genericIllegalStateException. Tests assert on the specific type, so a test cannot accidentally pass because some other error happened to be thrown.
Requires JDK 11.
./gradlew build # compile, test, checkstyle, spotbugs
./gradlew run # play in the console
./gradlew test# unit tests only
./gradlew cucumber # acceptance tests (Gherkin features)
./gradlew pitest # mutation testing — the slow, honest one
./gradlew jacocoTestReportCI (GitHub Actions, JDK 11 Temurin, Gradle 8.10) runs ./gradlew build on every push and
pull request to main, so style violations and SpotBugs findings fail the build rather than
accumulating.
| Card | Effect |
|---|---|
| Exploding Kitten | Drawing it removes you from the game unless you play a Defuse |
| Defuse | Cancels an Exploding Kitten; you choose where to bury it back in the deck |
| Attack | The next player takes two turns in a row |
| Skip | End your turn without drawing |
| Favor | A player of your choice gives you one card of their choosing |
| Shuffle | Shuffle the draw pile |
| See the Future | Privately view the top 2 cards, without rearranging |
| Draw From the Bottom | Draw from the bottom of the deck instead of the top |
| Reverse | Reverse play order; acts as a Skip in a 2-player game |
| Time Rewind | Swap the top 3 cards of the deck with the bottom 3 |
| Switch Deck by Half | Swap the top and bottom halves of the draw pile |
| Cat Cards | Two matching cats let you steal a random card from another player |
Course project for Software Quality Engineering (Northwestern University).
Qimeng Chao · Liming Luo · Shao Jin · Minjun Liang · Xiaoqin Bai
Java 11 · Gradle (Kotlin DSL) · JUnit 5 · Mockito · EasyMock · Cucumber · JaCoCo · PIT mutation testing · Checkstyle · SpotBugs · GitHub Actions