DRY double in RSpec

455 Views Asked by At

How to DRY(don't repeat yourself) the double in RSpec? For example:

let(:s1) {instance_double(Class1,
                            :attr1 => val1,
                            :attr2 => val2,
                            :attr3 => val3,
                            :attr4 => val4,
                            :attr5 => val5)}


let(:s2) {instance_double(Class1,
                            :attr1 => val0, # the only difference between s2 and s1
                            :attr2 => val2,
                            :attr3 => val3,
                            :attr4 => val4,
                            :attr5 => val5)}

let(:s3) {instance_double(Class1,
                            :attr1 => val6, # the only difference between s3 and s1
                            :attr2 => val2,
                            :attr3 => val3,
                            :attr4 => val4,
                            :attr5 => val5)}

These 3 doubles are very similar and it seems we can refactor. But I tried:

  1. to make a basic hash for them:

    basic_hash = {attr2 => val2, attr3 => val3, attr4 => val4, attr5 => val5}

And then modified this basic hash and pass it into instance_double, for example, pass it into :s1:

basic_hash_s1 = basic_hash.clone
basic_hash_s1[:attr1] = val1
let(:s3) {instance_double(Class1, basic_hash_s1)

But this doesn't work. I'm working on an existing project and the Rspec didn't give any error message(maybe the project has some setting?), it just jumped the whole spec file.

  1. I also tried to allow on double:

    allow(s1).to receive(:attr).and_return(val1)

It still doesn't work.

Somebody know how to refactor this? Thanks!

======= edit =======

I tried to create double Hash object and pass it into :s1, it can be passed, but I don't know how to modify the content in that double: I tried to modify it as the same way as modifying a regular Hash object but it didn't work.

2

There are 2 best solutions below

0
On BEST ANSWER
let(:opts) { { :attr1 => val1,
              :attr2 => val2,
              :attr3 => val3,
              :attr4 => val4,
              :attr5 => val5 } }

let(:s1) { instance_double(Class1, opts) }
let(:s2) { instance_double(Class1, opts.merge(:attr1 => val0)) }
let(:s3) { instance_double(Class1, opts.merge(:attr1 => val6)) }
4
On

Use a function

let(:create_s) do
    -> |attr1| do
         instance_double(Class1,
                        :attr1 => attr1
                        :attr2 => val2
                        :attr3 => val3
                        :attr4 => val4
                        :attr5 => val5)
    end
end

let(:s1) { create_s.call(val1) }
let(:s2) { create_s.call(val0) }
let(:s3) { create_s.call(val6) }