Help Me Refactor This Shared Example

63 views
Skip to first unread message

Karl

unread,
Aug 6, 2012, 5:16:51 PM8/6/12
to rs...@googlegroups.com
I would appreciate some help refactoring this shared_example.

describe "Sending Notifications" do
  before(:all) do
    create_basic_company_area_store # sets @company, @area, @store
    @user = FactoryGirl.create(:user)
    @company2 = FactoryGirl.create(:company)
  end

  shared_examples "sends all types" do
    it 'sends an email' do
      FactoryGirl.create(:email_notification, notification_type: notif_type, user: user)
      notification_email_with_expects(resource, expected_str)
    end
  end

  describe 'Something Bad Happens' do
    it_behaves_like "sends all types" do
      let(:notif_type) { NotificationType.find_by_name("bad stuff") }
      let(:user) { @user }
      let(:resource) { @company2 }
      let(:expected_str) { @company2.name }
    end
  end

  describe 'Something Even Worse Happens' do
    it_behaves_like "sends all types" do
      let(:notif_type) { NotificationType.find_by_name("really bad stuff") }
      let(:user) { @user }
      let(:resource) { @company2 }
      let(:expected_str) { @company2.name }
    end
  end

end

It works. But as I will need to test about 100 notification types, all those 'let' statements will be a bit repetitious. How can I write this and maybe knock it down to just 'it_behaves_like' single lines.

David Chelimsky

unread,
Aug 6, 2012, 5:51:49 PM8/6/12
to rs...@googlegroups.com
What actually needs to be different in each example? It seems like
you're using the same @user each time, so that could be expressed in
the shared example e.g.

shared_examples "sends all types" do
it 'sends an email' do
FactoryGirl.create(:email_notification,
notification_type: notif_type, user: FactoryGirl.create(:user))
notification_email_with_expects(resource, expected_str)
end
end

You can also move the notification type to the shared example - just
passing in the name used to look up the type:

shared_examples "sends all types" do |notification_type_name|
it 'sends an email' do
FactoryGirl.create(:email_notification,
notification_type: NotificationType.find_by_name(notification_type_name),
user: FactoryGirl.create(:user))
notification_email_with_expects(resource, expected_str)
end
end

describe 'Something Bad Happens' do
it_behaves_like "sends all types", "bad stuff" do
let(:resource) { @company2 }
let(:expected_str) { @company2.name }
end
end

Then it seems like @company2 can be declared within the example as well:


shared_examples "sends all types" do |notification_type_name|
it 'sends an email' do
company = FactoryGirl.create(:company)
FactoryGirl.create(:email_notification,
notification_type: NotificationType.find_by_name(notification_type_name),
user: FactoryGirl.create(:user))
notification_email_with_expects(company, company.name)
end
end

describe 'Something Bad Happens' do
it_behaves_like "sends all types", "bad stuff"
end

I'm sure I'm missing some things, but you get the idea.

Cheers,
David

Karl

unread,
Aug 6, 2012, 6:06:46 PM8/6/12
to rs...@googlegroups.com
David,
I should have been more clear...

1. The spec will be for each NotificationType. Somewhere south of 100, for now. Something like

  it_behaves_like "send all types", "bad stuff"
  it_behaves_like "send all types", "really bad stuff"
  ... x 80 or so...

2. I agree with your reply. I could move the @company2, @user into the shared example, but then it will create those records for every test, and I want to avoid that. In this particular case, it's just not necessary and slows the spec.

So, I really don't want to create those @company2 and @user records in every test. As you know, they will not exist out side the scope of the test, so I can't do:

 it_behaves_like "send all types", "bad stuff", @company, @user, ...

Ok, they don't instantiate until after the first spec is executed because they are in a before(:all). Is there a way to force them into existence, just once?


Thanks.

On Monday, August 6, 2012 2:51:49 PM UTC-7, dchel...@gmail.com wrote:

David Chelimsky

unread,
Aug 6, 2012, 10:09:20 PM8/6/12
to rs...@googlegroups.com
> David,
> I should have been more clear...
>
> 1. The spec will be for each NotificationType. Somewhere south of 100, for
> now. Something like
>
> it_behaves_like "send all types", "bad stuff"
> it_behaves_like "send all types", "really bad stuff"
> ... x 80 or so...
>
> 2. I agree with your reply. I could move the @company2, @user into the
> shared example, but then it will create those records for every test, and I
> want to avoid that. In this particular case, it's just not necessary and
> slows the spec.
>
> So, I really don't want to create those @company2 and @user records in every
> test. As you know, they will not exist out side the scope of the test, so I
> can't do:
>
> it_behaves_like "send all types", "bad stuff", @company, @user, ...
>
> Ok, they don't instantiate until after the first spec is executed because
> they are in a before(:all). Is there a way to force them into existence,
> just once?

Nothing built in. rspec-rails defers to Rails' transaction management,
which is designed to clean up after every example. You'd have to
disable that and roll your own using a tool like database cleaner.

Karl

unread,
Aug 8, 2012, 10:44:12 AM8/8/12
to rs...@googlegroups.com
Valdis, you are correct. But...

Unfortunately, they don't use the same code base. NotificationType is sort of a meta-class for quite a wide range of event types. And it's not the actually delivery I'm worried about, it's the matrix of event types and delivery methods in combination that is of concern.

At first, I did not test every event type. But I found that they did not always fire properly, mostly due to typos on my part. Most, but not all, are triggered in observers. And the observers are well spec'd. Nonetheless, I will sleep better testing every one of them.

What is not revealed by my example is that NotificationType events happen from something as simple as a user performing a destructive action, to a regression analysis of purchasing activity (think fraud detection).

Thanks for the suggestion.

On Tuesday, August 7, 2012 2:54:31 PM UTC-7, Valdis Pornieks wrote:
Karl,

Do you really need to call every single event type? Normally they would use the same code base thus calling only one of them would already cover them all.
Additionally you can test if sending a non-existent type causes appropriate error processing.

However if the result created by different messages differs, then this should be visible in the test scenarios, thus you would need to write different scenarios anyway.
Reply all
Reply to author
Forward
0 new messages