2
\$\begingroup\$

I have an rspec spec test that has two sets of the same tests (from 24 Pull Requests). I thought I'd refactor the spec whilst I fixed something else.

Normally I just do a basic each ... do array to DRY things up:

require 'spec_helper'

describe ReminderMailer, type: :mailer do

  # Half the code yay! :D
  ['daily','weekly'].each do |time_format|
    describe time_format do
      let(:time_format) { time_format }
      let(:user) do
        mock_model(User, nickname:           'David',
                         email:              'david@example.com',
                         languages:          ['Ruby'],
                         skills:             [],
                         pull_requests:      double(:pull_request, year: []),
                         suggested_projects: [])
      end

      # How do I do this bit better?  
      let(:mail) {
        if time_format == 'daily'
          ReminderMailer.daily(user)
        else
          ReminderMailer.weekly(user)
        end
      }

end

I couldn't figure out how to use the time_format with the send command, so I could do something like:

let(:mail) { ReminderMailer.send(timeformat(user) }

How could I do this?

\$\endgroup\$
5
\$\begingroup\$

The snippet you asked to re-write:

let(:mail) { ReminderMailer.send(time_format, user) }

Now, IMO using each to write specs is a bad practice. Why? Because rspec already provides idiomatic infrastructure to keep specs DRYed: use before and shared_examples. I'd write:

describe ReminderMailer, type: :mailer do
  let(:user) { ... }

  shared_examples "a reminder mailer" do |subject:, body:|
    it 'renders the subject' do
      expect(mail.subject).to eq(subject)
    end

    it 'renders the receiver email' do
      expect(mail.to).to eq([user.email])
    end

    it 'renders the sender email' do
      expect(mail['From'].to_s).to eq('24 Pull Requests <info@24pullrequests.com>')
    end

    it 'uses nickname' do
      expect(mail.body.encoded).to match(user.nickname)
    end

    it 'contains periodicity in body' do
      expect(mail.body.encoded).to match(body)
    end
  end

  describe 'daily' do
    let(:mail) { ReminderMailer.daily(user) }

    it_behaves_like "a reminder mailer", 
      subject: '[24 Pull Requests] Daily Reminder',
      body: 'today'
  end

  describe 'weekly' do
    let(:mail) { ReminderMailer.weekly(user) }

    it_behaves_like "a reminder mailer", 
      subject: '[24 Pull Requests] Weekly Reminder',
      body: 'week'
  end
end
\$\endgroup\$
  • \$\begingroup\$ Fantastic, even better than what I asked about :) \$\endgroup\$ – Peter Souter Dec 9 '14 at 20:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.