What do you do when you need to run the same test multiple times, but with different parameters? If you copy and paste the test, you end up with a hard-to-read test file. You can’t easily tell how the tests differ from one another. Worse, when you need to change one, you need to change them all. Take the following simple test file:
def double_it(number)
number * 2
end
describe '#double_it' do
it 'doubles 1 into 2' do
expect(double_it(1)).to eq(2)
end
it 'doubles 2 into 4' do
expect(double_it(2)).to eq(4)
end
it 'doubles 4 into 8' do
expect(double_it(4)).to eq(8)
end
end
There are a few ways we could reduce the duplication here. The one we will be using is Parameterized Test Method.
Reducing duplication with a Parameterized Test Method
Parameterized Test Method is a fancy-sounding name for a simple idea: a method, that takes parameters, and generates a test. Here’s how you use it:
- Take one of the tests and wrap it in a method. Then call the method in place of the test.
- Extract the unique test data to method arguments, and pass them in via the method call.
- Replace the remaining tests with calls to the new test method.
I’ll walk you through the refactoring, step-by-step:
Wrap the first example in a method and call it
describe '#double_it' do
def self.test_double_it
it 'doubles 1 into 2' do
expect(double_it(1)).to eq(2)
end
end
test_double_it
it 'doubles 2 into 4' do
expect(double_it(2)).to eq(4)
end
it 'doubles 4 into 8' do
expect(double_it(4)).to eq(8)
end
end
Extract the unique test data to method arguments
describe '#double_it' do
def self.test_double_it(initial, doubled)
it "doubles #{initial} into #{doubled}" do
expect(double_it(initial)).to eq(doubled)
end
end
test_double_it 1, 2
it 'doubles 2 into 4' do
expect(double_it(2)).to eq(4)
end
it 'doubles 4 into 8' do
expect(double_it(4)).to eq(8)
end
end
Replace the remaining tests with calls to the new test method
describe '#double_it' do
def self.test_double_it(initial, doubled)
it "doubles #{initial} into #{doubled}" do
expect(double_it(initial)).to eq(doubled)
end
end
test_double_it 1, 2
test_double_it 2, 4
test_double_it 4, 8
end
That’s it!
Why use the Parameterized Test Method
You might wonder why I didn’t just iterate over the expectation, or even over the entire it block. There are a few reasons:
- It’s more readable – I find using iteration a bit complex and less readable when setting up tests.
- Documentation – you get the exact same documentation format as before, with one line of documentation per example.
- Test failures are easier to understand – when a test fails, you know the exact test and line number that it failed on.
The next time you find yourself writing the same test over and over again, try refactoring the duplication away using a parameterized test method!