Reputation: 94
Consider this code snippet:
context 'votes' do
it { should_not be_able_to :vote_yes, create(:question, user: user), user: user }
it { should be_able_to :vote_yes, create(:question, user: other_user), user: user }
it { should_not be_able_to :vote_no, create(:question, user: user), user: user }
it { should be_able_to :vote_no, create(:question, user: other_user), user: user }
it { should_not be_able_to :reject_vote, create(:question, user: user), user: user }
it { should be_able_to :reject_vote, create(:question, user: other_user), user: user }
it { should_not be_able_to :vote_yes, create(:answer, user: user), user: user }
it { should be_able_to :vote_yes, create(:answer, user: other_user), user: user }
it { should_not be_able_to :vote_no, create(:answer, user: user), user: user }
it { should be_able_to :vote_no, create(:answer, user: other_user), user: user }
it { should_not be_able_to :reject_vote, create(:answer, user: user), user: user }
it { should be_able_to :reject_vote, create(:answer, user: other_user), user: user }
end
How to refactor into something with less lines and duplications?
Upvotes: 0
Views: 39
Reputation: 28285
The first thing I would do is split up your tests into more logical groupings, as it's currently very confusing (at a glance) what the expected behaviour is:
context 'voting yes' do
it { should be_able_to :vote_yes, create(:answer, user: other_user), user: user }
it { should_not be_able_to :vote_yes, create(:question, user: user), user: user }
it { should be_able_to :vote_yes, create(:question, user: other_user), user: user }
it { should_not be_able_to :vote_yes, create(:answer, user: user), user: user }
end
context 'voting no' do
it { should be_able_to :vote_no, create(:question, user: other_user), user: user }
it { should_not be_able_to :vote_no, create(:question, user: user), user: user }
it { should be_able_to :vote_no, create(:answer, user: other_user), user: user }
it { should_not be_able_to :vote_no, create(:answer, user: user), user: user }
end
context 'rejecting vote' do
it { should be_able_to :reject_vote, create(:question, user: other_user), user: user }
it { should_not be_able_to :reject_vote, create(:question, user: user), user: user }
it { should be_able_to :reject_vote, create(:answer, user: other_user), user: user }
it { should_not be_able_to :reject_vote, create(:answer, user: user), user: user }
end
Looking at this restructured list of tests, it is much easier to see a clear behavioural pattern. You could remove the repetition as follows:
%i(vote_yes vote_no reject_vote).each do |action_performed|
context "can #{action_performed} against other users" do
it { should be_able_to action_performed, create(:question, user: other_user), user: user }
it { should be_able_to action_performed, create(:answer, user: other_user), user: user }
end
context "cannot #{action_performed} against self" do
it { should_not be_able_to action_performed, create(:question, user: user), user: user }
it { should_not be_able_to action_performed, create(:answer, user: user), user: user }
end
end
You may even be tempted to take this one step further to remove duplication between question
and answer
tests:
%i(vote_yes vote_no reject_vote).each do |action_performed|
%i(question answer).each do |record_type|
it "can #{action_performed} against #{record_type} for other users" do
should be_able_to action_performed, create(record_type, user: other_user), user: user
end
it "cannot #{action_performed} against #{record_type} for self" do
should_not be_able_to action_performed, create(record_type, user: user), user: user
end
end
end
However, this probably makes the tests harder to understand and edit, so I'd advise against it... Maybe if that list of record types (question
, answer
, ...
) grows much longer, then you could consider an approach like this.
Upvotes: 2