factory_sloth
is too lazy to write to the database.
It finds unnecessary factory_bot create
calls and replaces them with less laborious build
or build_stubbed
calls to speed up your test suite.
bundle add factory_sloth
or gem install factory_sloth
.
Output of factory_sloth --help
:
Usage: factory_sloth [path1, path2, ...] [options]
Examples:
factory_sloth # run for all specs
factory_sloth spec/models
factory_sloth spec/foo_spec.rb spec/bar_spec.rb
Options:
-f, --force Ignore ./.factory_sloth_done
-l, --lint Dont fix, just list bad create calls
-u, --underscore Check underscore-prefixed variables
-V, --verbose Verbose output, useful for debugging
-v, --version Show gem version
-h, --help Show this help
factory_sloth
runs the changed specs to see if they still work. This takes a while for all possible changes - usually multiple times as long as the specs would normally take to run! For this reason, the gem creates a .factory_sloth_done
file. This is a list of specs that have already been processed. It makes it possible to interrupt the program and continue later. You can delete this file to start over or ignore it with -f
.
While running, factory_sloth
produces output like this:
🟡 spec/features/sign_up_spec.rb: 2 create calls found, 0 replaced
⚪️ spec/lib/string_ext_spec.rb: 0 create calls found, 0 replaced
spec/models/user_spec.rb:3:2: create replaced with build
expect(create(:user)).not_to be_nil
^^^^^^
spec/models/user_spec.rb:4:2: create_list replaced with build_list
expect(create_list(:user, 2).count).to eq 2
^^^^^^^^^^^
🟢 spec/models/user_spec.rb: 3 create calls found, 2 replaced
spec/weird_dir/crazy_spec.rb:8:4: create replaced with build_stubbed
expect(create(:user)).not_to be_nil
^^^^^^
🔴 spec/weird_dir/crazy_spec.rb: 33 create calls found, 0 replaced (conflict)
Scanned 4 files, found 2 unnecessary create calls across 1 files and 1 broken specs
The conflict
case is rare. It only happens if individual examples were green after changing them, but at least one example failed when evaluating the whole file after all changes. This probably means that some other example was red even before making changes, or that something else is wrong with this spec file, e.g. some examples depend on other examples' side effects.
factory_sloth
only works with RSpec so far. It also works best with unit tests such as model specs. It generates false positives in cases where create calls are done but only the absence of any effect is tested, e.g.:
user = create(:user)
User.delete_all
expect(User.count).to eq 0
This test will still pass if user
is never inserted into the database in the first place, leading factory_sloth
to believe that build
suffices here. However, this change makes the test no longer assert the same thing and reduces coverage. Magic comments can be used to prevent factory_sloth
from making such changes. factory_sloth
will not touch lines with inline # sloth:disable
comments, or sections framed in # sloth:disable
/ # sloth:enable
comments. Another option is to write the test in a different (and arguably more assertive) way, e.g.:
expect { User.delete_all }.to change { User.count }.from(1).to(0)
If you have a good idea about how to detect such cases automatically, let me know :)
Prefixing a variable name with an underscore is a common way of saying "I am creating this for side effects":
_user = create(:user)
Therefore factory_sloth
ignores such cases by default. Use the -u
flag to check such cases as well.
After checking out the repo, run bin/setup
to install dependencies. Then, run rake
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
Bug reports and pull requests are welcome on GitHub at https://github.com/jaynetics/factory_sloth.
The gem is available as open source under the terms of the MIT License.
- factory_trace finds unused factories
- rspectre finds unused test setup
- rubocop-factory_bot provides rubocop linters for factories