mutant ====== [![Build Status](https://github.com/mbj/mutant/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/mbj/mutant/actions/workflows/ci.yml) [![Gem Version](https://img.shields.io/gem/v/mutant.svg)](https://rubygems.org/gems/mutant) [![Gem Downloads](https://img.shields.io/gem/dt/mutant.svg)](https://rubygems.org/gems/mutant) [![GitHub Stars](https://img.shields.io/github/stars/mbj/mutant.svg)](https://github.com/mbj/mutant) [![Discord](https://img.shields.io/discord/767914934016802818.svg)](https://discord.gg/BSr62b4RkV) ## What is Mutant? **AI writes your code. AI writes your tests. But who tests the tests?** Copilot, Claude, and ChatGPT generate code faster than humans can review it. They'll even write tests that pass. But passing tests aren't the same as *meaningful* tests. Mutant is mutation testing for Ruby. It systematically modifies your code and verifies your tests actually catch each change. The more code AI writes for you, the more you need verification you can trust. ## What is an Alive Mutation? Each surviving (alive) mutation is a call to action with exactly one of two options: - **Keep the mutated code** - your tests already specify the correct semantics, and the original code is redundant. Accept the mutation as a simplification. - **Add the missing test** - the original code is correct, but the tests don't verify the behavior the mutation removed. ## Author Mutant was created and is developed by [Markus Schirp](https://schirp-dso.com). It is the subject of [IEEE-published research](https://ieeexplore.ieee.org/document/7107453/) and is included in the [Trail of Bits Ruby Security Field Guide](https://trailofbits.github.io/rubysec/mutant/index.html). ## Quick Start ```ruby # lib/person.rb class Person def initialize(age:) @age = age end def adult? @age >= 18 end end ``` ```ruby # spec/person_spec.rb RSpec.describe Person do describe '#adult?' do it 'returns true for age 19' do expect(Person.new(age: 19).adult?).to be(true) end it 'returns false for age 17' do expect(Person.new(age: 17).adult?).to be(false) end end end ``` Tests pass. But run mutant: ```bash gem install mutant-rspec mutant run --use rspec --usage opensource --require ./lib/person 'Person#adult?' ``` Mutant finds a surviving mutation indicating a shallow test: ```diff def adult? - @age >= 18 + @age > 18 end ``` Your tests don't cover `age == 18`. The mutation from `>=` to `>` doesn't break them. This is just one of many mutation operators. Mutant also mutates arithmetic, logical, bitwise operators, removes statements, modifies return values, and more. A full working example is available in the [quick_start](quick_start/) directory. ## Session History Mutant records every run to `.mutant/results/`. You can recall past results without re-running mutation testing: ```bash # List past sessions (most recent first) mutant session list # Show full report from the latest session mutant session show # Show full report from a specific session mutant session show --session-id 019cf6f1-77e8-74b6-82db-f8b5faf570cd # List subjects with alive/total mutation counts mutant session subject # Show alive mutations for a specific subject mutant session subject 'Foo::Bar#baz' # Remove old or incompatible session files mutant session gc --keep 50 ``` ## Next Steps 1. Learn the [nomenclature](/docs/nomenclature.md) (subjects, mutations, operators) 2. Set up your [integration](/docs/nomenclature.md#integration): [RSpec](/docs/mutant-rspec.md) or [Minitest](/docs/mutant-minitest.md) 3. Run mutant on CI in [incremental](/docs/incremental.md) mode ## Ruby Versions Mutant is supported on Linux and macOS. | Version | Runtime | Syntax | Mutations | | ------- | ------- | ------ | --------- | | 3.2 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | 3.3 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | 3.4 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | 4.0 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | ## Rails Mutant runs against Rails applications, including parallel-worker database isolation (PostgreSQL and SQLite). The hook recipes are verified in CI on every non-EOL Rails version: | Version | Status | | ------- | ------ | | 7.2 | :heavy_check_mark: | | 8.0 | :heavy_check_mark: | | 8.1 | :heavy_check_mark: | See the [Rails Integration guide](/docs/rails.md) for setup, eager loading, and per-worker database isolation. A runnable, CI-verified example lives in [rails_example](rails_example/). ## Licensing **Free for open source.** Use `--usage opensource` for public repositories. **Commercial use** requires a subscription ($30/month or $250/year per developer). **Enterprise** — [reach out directly](mailto:info@schirp-dso.com?subject=Mutant%20Enterprise). See [commercial licensing](/docs/commercial.md) for pricing and details. ## Documentation * [Configuration](/docs/configuration.md) * [RSpec Integration](/docs/mutant-rspec.md) * [Minitest Integration](/docs/mutant-minitest.md) * [Rails Integration](/docs/rails.md) * [Incremental Mode](/docs/incremental.md) * [Reading Reports](/docs/reading-reports.md) * [Concurrency](/docs/concurrency.md) * [AST Pattern Matching](/docs/ast-pattern.md) * [Session JSON Schema](/docs/session-json-schema.yml) * [Hooks](/docs/hooks.md) * [Sorbet](/docs/sorbet.md) * [Nomenclature](/docs/nomenclature.md) * [Limitations](/docs/limitations.md) * [Mutant in the Wild](/docs/in-the-wild.md) ## Communication * [Discord](https://discord.gg/BSr62b4RkV) * [GitHub Issues](https://github.com/mbj/mutant/issues) * [Release Announcements](https://announce.mutant.dev/signup) ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines. ## Acknowledgments * [Contributors](https://github.com/mbj/mutant/graphs/contributors) * The `mutant-minitest` integration was sponsored by [Arkency](https://arkency.com/)