Git Product home page Git Product logo

noncommittal's Introduction

Noncommittal

This gem helps you avoid test pollution in your Rails test suite by preventing tests from committing records to your Postgres database.

How to avoid commitment

Add this to your Gemfile, probably grouped with other test gems:

group :test do
  gem "noncommittal"
end

Then, in your test_helper.rb (or similar), after you require your Rails environment, just drop this in:

Noncommittal.start!

This will create an empty table called noncommittal_no_rows_allowed and, for every table in your database, a deferred foreign key constraint that will effectively prevent any records from being committed outside the test transaction.

Do you have commitment issues?

By default, Ruby on Rails tests run each test in a transaction that is rolled back at the end of each test. This is a performant way to create proper isolation, preventing tests that save records to the database from affecting the environment of your other tests.

However, Rails can only enforce this constraint when the test-scoped code connects to the database via the framework and within that transaction. If a test were to extend Minitest::Test instead of ActiveSupport::TestCase, that test would not benefit from this rollback-only transaction protection. And if the subject under test contains transaction logic itself, or creates its own database connections, or spawns child processes, then it's entirely possible that some of your tests will erroneously commit records to the database, potentially causing test pollution.

Over the years, Rubyists have taken several approaches to mitigate this risk, but most popular solutions have drawbacks:

  • Gems like database_cleaner that purge your database between tests introduce a per-test runtime cost that is much higher than relying on transaction rollbacks
  • Bisecting your test suite to identify which test that violates transaction safety catches test pollution only after it causes a problem and is separately time-consuming
  • Adding a before-hook that runs before each test to ensure tables are empty won't give you a stack trace to the test that managed to commit inserted records

So that's why the noncommittal gem exists! It's fast, will catch this problem as soon as it's introduced, and will give you an accurate stack trace to the offending test.

What if I want to commit to certain tables?

By default, noncommittal will gather the table names of all your models that descend from ActiveRecord::Base, but this may not be what you want (you might want to exclude certain models or include additional tables). To override this behavior, you can pass an array of table names to a tables keyword argument, like so:

Noncommittal.start!(tables: [:users, :posts])

If you simply want to exclude certain tables, you can set the exclude_tables keyword argument:

Noncommittal.start!(exclude_tables: [:system_configurations])

What if I want to stop disallowing committed inserts?

Just call Noncommittal.stop!, which takes the same arguments as start! for specifying tables and exclusions, and will simply remove all the constraints and the reference table.

Limitations

This only works with Postgres currently. PRs welcome if you can accomplish the same behavior with other database adapters!

Acknowledgements

This gem is a codification of this tweet, which itself was the brainchild of Matthew Draper.

Code of Conduct

This project follows Test Double's code of conduct for all community interactions, including (but not limited to) one-on-one communications, public posts/comments, code reviews, pull requests, and GitHub issues. If violations occur, Test Double will take any action they deem appropriate for the infraction, up to and including blocking a user from the organization's repositories.

noncommittal's People

Contributors

searls avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.