Reputation: 5233
I have feature test, which have 2 duplicated parts:
require 'spec_helper'
describe "Messages manage" do
let (:user) { create :user }
let (:other_user) { create :use }
before(:each) do
login_as user
end
describe "[message create]" do
it "messages created by self, should appear on the page", js: true do
message_text = "Hello my friend"
visit user_messages_path(user, {with:other_user.id} )
fill_in :message_body, with: message_text
click_button t("users.messages.index.send")
sleep 1 # waiting for js
page.should have_content(message_text)
find_field(:message_body).value.should == ""
page.should have_selector('#message_body', visible: false)
end
it "message errors should be displayed", js: true do
message_text = "123"
visit user_messages_path(user, {with:other_user.id} )
fill_in :message_body, with: message_text
click_button t("users.messages.index.send")
sleep 1 # waiting for js
page.should_not have_content("Message: #{message_text}")
find_field(:message_body).value.should == message_text
page.should have_selector('.message.error', visible: true)
end
end
end
What is the best way using of shared_examples or something like that, in this situation? To following DRY.
Upvotes: 3
Views: 1053
Reputation: 16793
I don't think you have quite enough similar code to necessitate a shared_examples_for
block (though an argument could be made for a utility method in spec/support/utilities.rb), so instead I attempted to split it out a bit.
It's difficult to quantitatively give you a right answer for this question as a lot depends on your style of coding, as per the discussion around @BillyChan's answer. My style is to attempt to be as curt yet readable as possible; others may think it's too DRY, some not DRY enough. Don't know if this actually works as I don't have your code, but for your consideration...
require 'spec_helper'
describe "Message management" do
let(:user) { create :user }
let(:other_user) { create :user }
before { login_as user }
subject { page }
describe "message creation" do
let(:send_button) { t("users.messages.index.send") }
before { visit user_messages_path(user, { with: other_user.id }) }
context "by self", js: true do
let(:message_text) { "Hello my friend" }
before do
fill_in :message_body, with: message_text
click_button send_button
sleep 1 # waiting for js
end
it { should have_content(message_text) }
it { should have_selector('#message_body', visible: false) }
specify { find_field(:message_body).value.should == "" }
end
describe "message errors", js: true do
let(:message_text) { "123" }
before do
fill_in :message_body, with: message_text
click_button send_button
sleep 1 # waiting for js
end
it { should_not have_content("Message: #{message_text}") }
it { should have_selector('.message.error', visible: true) }
specify { find_field(:message_body).value.should == message_text }
end
end
end
Edit Further explanation as requested by @ole:
specify
is an alias for it
. I use specify
here because I think the phrase reads better (you could just as easily substitute it out for it
). Also, the conditions in the it
blocks relate to the subject
of page
, while the content in the specify
block is essentially not referring directly to the page
and is "changing the subject
" of the test. If I wanted to put the code from the specify
blocks into what I think is a readable, though a bit more verbose, it
block, I'd probably change it to something like:
describe "message errors", js: true do
let(:message_text) { "123" }
before do
fill_in :message_body, with: message_text
click_button send_button
sleep 1 # waiting for js
end
it { should_not have_content("Message: #{message_text}") }
it { should have_selector('.message.error', visible: true) }
describe "message body value" do
let(:message_body) { find_field(:message_body).value }
subject { message_body }
it { should == message_text }
end
end
You could also remove the let
statement and put find_field(:message_body).value
directly into the subject
block if you wanted. It's all a matter of taste and style. Is this syntax appropriate for this example? You decide :-)
Upvotes: 2
Reputation: 24815
You can append a specific method right under the code
require 'spec_helper'
describe "Messages manage" do
# let (:user) { create :user }
# Instead expose it as instance variable
@user = create(:user)
# let (:other_user) { create :user }
@other_user = create(:user)
before(:each) do
login_as user
end
describe "[message create]" do
it "messages created by self, should appear on the page", js: true do
# Use the custom method
send_message "Hello my friend"
page.should have_content(message_text)
find_field(:message_body).value.should == ""
page.should have_selector('#message_body', visible: false)
end
it "message errors should be displayed", js: true do
# Use the custom method
send_message "123"
page.should_not have_content("Message: #{message_text}")
find_field(:message_body).value.should == message_text
page.should have_selector('.message.error', visible: true)
end
end
def send_message(message_text)
visit user_messages_path(@user, {with:@other_user.id} )
fill_in :message_body, with: message_text
click_button t("@users.messages.index.send")
sleep 1 # waiting for js
end
end
For more general code, you can extract them into spec/support in a module. But the above method should be good enough for this case.
Upvotes: 3