Yutaka's blog

Why should we write tests redundantly?

During the development of Ruby on Rails applications, we often fall into the pitfalls of writing tests in a DRY way. DRY might be the most well-known principle of software development. Developers are likely to follow the guide and try to write DRY codes immediately whenever they find some "duplications." Of course, DRY is important in some situations, but I want to articulate this phrase.

Don't make your tests DRY excessively.

As Testing on the Toilet: Tests Too DRY? Make Them DAMP! describes, code readers have to do some mental computation to understand the tests themselves. When other developers refactor codes and hit the failure of DRY tests, they must move their eyes up and down. Here is an example of the situation; this code uses subject and makes it expect the variable named a to be declared with let. It requires developers to understand how subject is declared and what is a.

Bad:

describe SomeModel, type: :model do
  describe '#do_something' do
    subject { instance.do_something(a) }

    let(:instance) { SomeModel.new }

    # Many lines appear...

    context 'when a is true' do
      let(:a) { true }

      it 'gets true' do
        expect(subject).to be true
      end
    end

    context 'when a is false' do
      let(:a) { false }

      it 'gets false' do # <-- If this test fails, developers must understand what `subject` is, and they must move their eyes upward.
        expect(subject).to be false
      end
    end
  end
end

On the other hand, the code below is good because code readers don't have to care about external variables out of the test case.

Good:

describe SomeModel, type: :model do
  describe '#do_something' do
    it 'gets true' do
      expect(SomeModel.new.do_something(true)).to be true
    end

    it 'gets false' do # <-- It's obvious because `do_something` and its parameter are put into this test case
      expect(SomeModel.new.do_something(false)).to be false
    end
  end
end

By the way, Testing on the Toilet: Tests Too DRY? Make Them DAMP! also explains helper functions. Helper functions are sometimes necessary to make tests readable. Yes, they are DRY codes, but I think it's good to go with them as long as they help us to reduce cognitive load. For example, take_screenshot is a good DRY and helpful since it's descriptive and meaningful; it's easy to comprehend just by seeing the method name.

That's what I wanted to say here. Please don't make your tests DRY excessively, or other developers, including you in the future, will have to use their energy to understand the DRY codes and get lost in why tests fail.