Build Status
ActiveRecordBlockMatchers
Custom RSpec matchers for ActiveRecord record creation
Installation
Add this line to your application's Gemfile:
gem 'active_record_block_matchers'
And then execute:
$ bundle
Or install it yourself as:
$ gem install active_record_block_matchers
Quick Examples
expect {
post :create, user: { username: "bob", password: "BlueSteel45" }
}.to create_a(User)
.with_attributes(username: "bob")
.which {|bob| expect(AuthLibrary.authenticate("bob", "BlueSteel45")).to eq bob }
expect {
post :create, user: { username: "bob", password: "BlueSteel45" }
}.to create(User => 1, Profile => 1)
.with_attributes(
User => [{username: "bob"}],
Profile => [{avatar_url: Avatar.default_avatar_url}],
).which { |new_records_hash|
new_user = new_records_hash[User].first
new_profile = new_records_hash[Profile].first
expect(new_user.profile).to eq new_profile
}
Detailed Examples
create_a
aliases: create_an
, create_a_new
Example:
expect { User.create! }.to create_a(User)
This can be very useful for controller tests:
expect { post :create, user: user_params }.to create_a(User)
You can chain .with_attributes
as well to define a list of values you expect the new object to have. This works with both database attributes and computed values.
expect { User.create!(username: "bob") }
.to create_a(User)
.with_attributes(username: "bob")
This is a great way to test ActiveReocrd hooks on your model. For example, if your User model downcases all usernames before saving them to the database, you can test it like this:
expect { User.create!(username: "BOB") }
.to create_a(User)
.with_attributes(username: "bob")
You can even use RSpec's composable matchers:
expect { User.create!(username: "bob") }
.to create_a(User)
.with_attributes(username: a_string_starting_with("b"))
If you need to make assertions about things other than attribute equality, you can also chain .which
with a block, and your block will receive the newly created record:
expect { User.create!(username: "BOB", password: "BlueSteel45") }
.to create_a(User)
.which { |user|
expect(user.encrypted_password).to be_present
expect(AuthLibrary.authenticate("bob", "BlueSteel45")).to eq user
}
Gotcha Warning: Be careful about your block syntax when chaining .which
in your tests. If you write the above example with a do...end
, the example will parse like this: expect {...}.to(create_a(User).which) do |user| ... end
, so your block will not execute, and it may appear that your test is passing, when it is not.
This matcher relies on a created_at
column existing on the given model class. The name of this column can be configured via ActiveRecordBlockMatchers::Config.created_at_column_name = "your_column_name"
create
aliases: create_records
Example:
expect { User.create!; User.create!; Profile.create! }
.to create(User => 2, Profile => 1)
Just like the other matcher, you can chain with_attributes
and which
to assert about the particulars of the records:
expect { UserService.sign_up!(username: "bob", password: "BlueSteel45") }
.to create(User => 1, Profile => 1)
.with_attributes(
User => [{username: "bob"}],
Profile => [{avatar_url: Avatar.default_avatar_url}]
).which { |records|
# records is a hash with model classes for keys and the new records for values
new_user = records[User].first
new_profile = records[Profile].first
expect(AuthLibrary.authenticate("bob", "BlueSteel45")).to eq new_user
expect(new_user.profile).to eq new_profile
}
As noted, the which
block yields a hash containing the new records whose counts were specified.
Order doesn't matter for the attributes specified in with_attributes
, but you must provide an attribute hash for every record that was created. This means, if you expect the block to create, say 2 User records, you must provide an attributes hash for each new User record:
# This is correct:
expect { User.create!(username: "bob"); User.create!(username: "rhonda") }
.to create(User => 2)
.with_attributes(
User => [{username: "rhonda"}, {username: "bob"}]
)
# This will raise an error:
expect { User.create!(username: "bob"); User.create!(username: "rhonda") }
.to create(User => 2)
.with_attributes(
User => [{username: "rhonda"}]
)
# But this is totally fine if you really need a workaround:
# Just put the empty hashes last
expect { User.create!(username: "bob"); User.create!(username: "rhonda") }
.to create(User => 2)
.with_attributes(
User => [{username: "rhonda"}, {}]
)
Development
After checking out the repo, run bin/setup
to install dependencies. Then, 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
.
Contributing
- Fork it ( https://github.com/[my-github-username]/active_record_block_matchers/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request