Rspec: implicitly defined subject - ruby-on-rails

Rspec implicitly defined subject documentation says:
While the examples below demonstrate how subject can be used as a
user-facing concept, we recommend that you reserve it for support of
custom matchers and/or extension libraries that hide its use from
examples.
Does it mean, that I should try to never call "subject." directly in my specs? If yes, what should I use instead as a subject object?

Compare these 2 examples:
describe "User" do
subject { User.new(age: 42) }
specify { subject.age.should == 42 }
its(:age) { should == 42 }
end
describe "User" do
let(:user) { User.new(age: 42) }
specify { user.age.should == 42 }
end
UPDATE
There is a cool feature in Rspec - named subject:
Here is an example from David Chelimsky:
describe CheckingAccount, "with a non-zero starting balance" do
subject(:account) { CheckingAccount.new(Money.new(50, :USD)) }
it { should_not be_overdrawn }
it "has a balance equal to the starting balance" do
account.balance.should eq(Money.new(50, :USD))
end
end
When you use user instead of a subject it's more readable (IMHO).
But subject lets you use nice extension its(:age).

Related

How to pass variables with the same name in rspec's shared_examples?

Let's imagine I have some tests and want to use shared examples, e.g.:
RSpec.shared_examples "shared examples" do
let(:x) { "" }
it "should be equal to 5" do
expect(x).to eq(5)
end
end
and then use it like:
describe "my tests" do
let(:x) { 5 }
it_behaves_like "shared examples" do
let(:x) { x }
end
end
I know that I can do that implicitly, without passing let(:x) { x } to the child block and everything will work. But what I'm trying to achieve - is to add more clarity to my tests.
So the question is: how to pass a variable (override if you want) with the same name without falling into maximum call stack error to a child shared examples block?
Please let me know if my approach is not right in general.
You just need to define the variable inside blocks, let is lazy-evaluated, so it is not evaluated until the first time
the method it defines is invoked, but they need to be inside blocks like context, something like this:
RSpec.describes "shared examples" do
context 'when x has value' do
let(:x) { 5 }
it "should be equal to 5" do
expect(x).to eq(5)
end
end
context 'when x is empty' do
let(:x) { '' }
it "should be empty" do
expect(x).to eq('')
end
end
end

Rspec : Custom Matchers : want a matcher with behavior just opposite of existing matcher

I have a situation
let(:user) { create(:user, organization: org, role_ids: [Role::ROLE_CSA]) }
subject(:ability) { Ability.new(user) }
describe 'Not Permitted' do
# I need this
it { is_expected_not_to (be_able_to(:index, AdminsController)) }
# Or this
it { is_expected_to (not_be_able_to(:index, AdminsController)) }
end
But unfortunately I found that the is_expected_not_to and not_be_able_to are not available.
I could do in this way
it 'should not permit' do
expect(ability).not_to be_able_to(:index, AdminsController)
end
But If possible I would like to use the short form instead. Is there any way I can achieve what I want?
If not, I would like to create new custom matcher which has behavior just opposite to be_able_to of CanCan or is_expected_to if possible. can anybody help?
from: https://www.relishapp.com/rspec/rspec-core/docs/subject/one-liner-syntax
You should be able to do:
it { is_expected.not_to (be_able_to(:index, AdminsController)) }

The data must be identical, and the test should be successful. Data are identical, but the test failed

I am testing method last_photo:
def last_photo
#last_photo ||= user_updates.latest.where("photo_front IS NOT NULL and photo_front != ''").first.try(:photo_front)
end
Spec:
context "instance method" do
let(:user) { create :user }
context "last photo" do
before { create_list(:user_update, 3, user: user) }
let(:user_updates){ user.user_updates }
describe "#last_photo" do
subject { user.last_photo }
it { should eq user_updates.latest.first.photo_front }
end
end
end
the test should be successful. But there are strange error.
Attached GIST.
The answer is pretty simple really:
expected: #<PhotoUploader:0x00000007e34868 ...
got: #<PhotoUploader:0x00000007ebc100 ...
The values might be the same, but the objects are different in memory. Since you're doing a comparison on the objects, rspec expects the objects to be the exact same.
Now, user.user_updates and user_updates are two different variables in memory. You should do a comparison on the values.

How to define Rspec custom matcher for its(:field)

I want to declare custom matcher for Rspec 2
I am using rspec 2.13 and rails 3.2.13.
I have tried to write something like this:
RSpec::Matchers.define :be_present do |expected|
match do
expected !be_empty
end
end
but when I use this in spec, it doesn't works
Failures:
1) NewsletterMailer.send_newsletter_to_groups from
Failure/Error: its(:from) { should be_present }
ArgumentError:
wrong number of arguments (1 for 0)
Spec code:
describe NewsletterMailer do
describe '.send_newsletter_to_emails' do
let(:user) { create(:admin) }
let(:user2) { create(:user) }
subject { NewsletterMailer.send_newsletter_to_emails(newsletter.id, "#{user.email}, #{user2.email}") }
its(:to) { should == [user.email, user2.email] }
its(:from) { should be_present }
its(:subject) { should be }
end
Edit:
I want to have reverse of logic like this:
its(:from) { should_not be_nil }
I'm not sure why you need a custom matcher at all here. Wouldn't
its(:from) { should be }
work for you?
See here:
https://www.relishapp.com/rspec/rspec-expectations/v/2-3/docs/built-in-matchers/be-matchers#be-matcher
obj.should be # passes if obj is not nil
Update:
Since apparently the question is how to write a custom matcher for an existing predicate present?, then the answer is: rspec already provides that, and there is still no need to write a custom matcher.
https://www.relishapp.com/rspec/rspec-expectations/v/2-3/docs/built-in-matchers/predicate-matchers
For any predicate #foo? on an object, you can just write should be_foo. Rspec will even define matchers for predicates that start with "has" like has_foo? with a more natural syntax, so that you can just write should have_foo.
Solution:
RSpec::Matchers.define :be_present do |expected|
match do |actual|
actual && actual.present?
end
end
Looks like this helper already exists in Rspec.
I just reinvented wheel. But I will still let this answer and don't delete this post.
It will be tip for developers, how to declare custom matcher without parameter eg.
be_something_special

Simplifying and correct RSpec controller tests

I have a few RSpec controller tests. Some work, some don't, and I'm trying to figure out how on Earth to fix them up and make them more efficient
Ideally, I would like to see if I can get each spec into the following form
subject { ... }
it { ... }
it { ... }
it { ... }
Note that for all of my controller specs I've written macros for the actual controller actions. The macros are all tested and all work, and the names make it fairly obvious what they do.
My "Create" test:
formats ||= ["html", "js"]
formats.each do |format|
context "valid attributes" do
subject { do_post_create( :customer, valid_attributes, format ) }
its(:response_code) { should eq(302)}
it { should redirect_to admin_customer_path(Customer.find_by_id(???))}
it { expect { subject }.to change(Customer, :count).by(1) }
end
context "invalid attributes" do
subject { do_post_create( :customer, invalid_attributes, format ) }
its(:response_code) { should eq(200)}
it { should render_template :new }
it { expect { subject }.to_not change(Customer, :count).by(1) }
end
end
In that spec, I've been trying to figure out some way to get the ID of the newly created object from the post statement. I've tried "Customer.last", but that doesn't seem to work. Any thoughts?
My "Update" spec:
formats ||= ["html", "js"]
formats.each do |format|
context "valid attributes" do
let(:object) { FactoryGirl.create(:customer) }
subject { do_put_update( class_to_symbol(model), object.id, attributes, format ) }
its(:response_code) { should eq(302)}
it "does alter #{model}" do
do_put_update( class_to_symbol(model), object.id, attributes, format )
assigns(:customer).should eq(object)
flash[:notice].should =~ /Success/
object.reload
attributes.each do |key, value|
object.send(key.to_s).should eq(value)
end
end
end
context "invalid attributes" do
let(:object) { FactoryGirl.create("customer") }
let(:invalid_attributes) { {:username => "!"} }
subject { do_put_update( class_to_symbol(model), object.id, invalid_attributes, format ) }
its(:response_code) { should eq(200)}
it "does not alter #{model}" do
do_put_update( class_to_symbol(model), object.id, invalid_attributes, format )
assigns(:customer).should eq(object)
flash[:notice].should =~ /Fail/
object.reload
attributes.each do |key, value|
object.send(key.to_s).should_not eq(value)
end
end
end
end
In the Update test, I would like to try to express the second block in a more concise way, ideally in a way that I can use the same "subject" statement for all of the tests. Is that possible?
I think you're over-thinking these specs. Instead of trying to force every spec into a predefined format (subject/it/...) write the specs so that they clearly document what should happen, then try to refactor the code afterwards.
Case in point: the use of the implicit subject for controller actions. subject and its are meant to be used with an object, not a method, and only really make sense when used that way. So for example, this makes sense:
subject { [1, 2, 3, 4] }
its(:size) { should == 4 }
Here, it's absolutely clear what is being tested: a 4-element array has a size of 4.
However, when you write:
subject { do_post_create( :customer, valid_attributes, format ) }
its(:response_code) { should eq(302)}
it's not really clear where you are getting that response code from without inspecting the do_post_create action. You say that the names of the macros "make it fairly obvious what they do", but they don't make it fairly obvious what they will return, and this is key for using the implicit subject because it's the return value that becomes the subject.
It would be much clearer just to write:
it "responds with a 302" do
do_post_create(:customer, valid_attributes, format)
response.should eq(302)
end
I also don't recommend mixing specs with and without implicit subjects, since it makes it yet more confusing what you are actually testing. In your invalid attributes context block, for example, you set a subject, but then in your second spec you actually test assignment of customer (assigns(:customer).should eq(object)), so basically the subject is irrelevant for this test. (However by setting the subject here and then not using it you are actually sending a PUT request twice (through do_put_update), which is bound to cause problems -- again, another reason not to be making requests in a subject block.)
I could go on, but I think you get the picture. Making specs short and sweet is great if you can do it without hurting readability, but in this case I think you've gone overboard.
Just my two cents, hope it helps.
p.s. In case the views above seem a bit extreme, read the documentation for implicit subjects, where you'll see that they actually recommend against using implicit subjects at all in public-facing tests:
While the examples below demonstrate how subject can be used as a user-facing concept, we recommend that you reserve it for support of custom matchers and/or extension libraries that hide its use from examples.

Resources