Moots is a simple, open-source mutation testing tool for Ruby, inspired by the mutant gem but with a more focused scope. It helps you improve your test suite by introducing small changes (mutations) to your code and checking if your tests can detect these changes.
Mutation testing is a method of evaluating the quality of your test suite. It works by:
- Making small changes (mutations) to your code
- Running your test suite against the mutated code
- Checking if your tests can detect these changes
If your tests fail when the code is mutated, that's good! It means your tests are catching potential bugs. If your tests pass with mutated code, it suggests your test coverage might need improvement.
While there are other mutation testing tools available for Ruby (like mutant), Moots aims to be:
- Simple: Focused on core mutation testing features
- Fast: Optimized for quick feedback cycles
- Open Source: Fully MIT licensed and community-driven
- Rails-friendly: Designed with Ruby on Rails applications in mind
Add this line to your application's Gemfile:
gem 'moots'And then execute:
bundle installTo run mutation testing on your codebase:
bundle exec mootsBy default, Moots will:
- Analyse your test suite
- Generate mutations for your code
- Run your tests against each mutation
- Report which mutations were caught and which weren't
Create a .moots.yml file in your project root to configure Moots:
# Example configuration
include:
- app/**/*.rb
- lib/**/*.rb
exclude:
- app/models/concerns/**/*.rb
test_command: bundle exec rspecLet's break down exactly how Moots performs mutation testing, step by step:
Moots starts by scanning your codebase for Ruby files based on your configuration:
- It uses Ruby's
Dir[]glob pattern matching to find files matching yourincludepatterns - It excludes any files matching your
excludepatterns - It checks each file is readable before processing
For each Ruby file, Moots:
- Reads the file content
- Uses the parser gem to convert your Ruby code into an Abstract Syntax Tree (AST)
- An AST is a tree representation of your code's structure
- This lets Moots understand your code's meaning, not just its text
Moots walks through the AST looking for code it can mutate. Currently, it handles:
a) Arithmetic Operators:
- Changes
+to- - Changes
-to+ - Changes
*to/ - Changes
/to*
b) Boolean Operators:
- Changes
&&to|| - Changes
||to&&
For example, this code:
def add(a, b)
a + b
endBecomes:
def add(a, b)
a - b # The + operator was mutated to -
endFor each mutation, Moots:
- Temporarily modifies your source file with the mutation
- Runs your test command (e.g.,
bundle exec rspec) - Captures the test output and result
- Restores your original code
- Records whether the mutation was "killed" (tests failed) or "survived" (tests passed)
Moots includes several safety features:
- Always restores your original code, even if tests fail
- Handles file read/write errors gracefully
- Skips unreadable or invalid files
- Uses Ruby's
Open3.capture3to safely run test commands
- Abstract Syntax Trees (AST)
- Ruby Parser Documentation
- Mutation Testing Paper by Alex Groce et al.
- Why Mutation Testing? - Google Testing Blog
Moots is built with a clean, modular architecture. Here's how the different components work together:
-
Moots(main.rb)- The entry point of the application
- Orchestrates the mutation testing process
- Handles configuration loading and file processing
- Manages the overall mutation testing workflow
-
MutationGenerator(mutation_generator.rb)- Responsible for analyzing Ruby code and generating mutations
- Uses the parser gem to build ASTs
- Walks through the AST to find mutation points
- Generates different types of mutations (arithmetic, boolean)
- Handles file path inclusion/exclusion logic
-
TestRunner(test_runner.rb)- Executes test commands against mutated code
- Captures and processes test output
- Handles test command execution safely
- Manages stdout/stderr capture
- Determines if mutations were killed or survived
-
Configuration(configuration.rb)- Manages loading and validation of
.moots.yml - Handles default configuration values
- Validates include/exclude patterns
- Manages test command configuration
- Manages loading and validation of
-
Mutation(mutation.rb)- Represents a single code mutation
- Stores mutation location and type
- Handles mutation application and reversion
- Tracks mutation status (killed/survived)
-
FileHandler(file_handler.rb)- Manages file operations
- Handles file reading and writing
- Ensures safe file modifications
- Manages file backup and restoration
Mootsloads configuration and starts the processMutationGeneratoranalyzes files and creates mutations- For each mutation:
FileHandlerbacks up the original fileMutationapplies the changeTestRunnerruns the testsFileHandlerrestores the original file
- Results are collected and reported
This modular design makes it easy to:
- Add new mutation types
- Support different test runners
- Extend configuration options
- Handle different file types
- Add new features
After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/OkayDave/moots. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
The gem is available as open source under the terms of the MIT License.
Everyone interacting in the Moots project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.