Test smell: Obscure Test

by Jason Swett,

You’ve probably heard of the idea of a “code smell” – a hint that something in the code is not quite right and ought to be changed.

Just as there are code smells, there are “test smells”. The book xUnit Test Patterns describes a number of them.

One of the smells described in the book is Obscure Test. An Obscure Test is a test that has a lot of noise in it, noise that’s making it hard to discern what the test is actually doing.

Here’s an example of an Obscure Test I wrote myself:

context 'the element does not exist' do
  before do
    contents = %(
      <?xml version="1.0" encoding="UTF-8"?>
      <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
        <channel>
          <item></item>
        </channel>
      </rss>
    )

    xml_doc = Nokogiri::XML(contents)
    episode_element = xml_doc.xpath('//item').first
    @rss_feed_episode = RSSFeedEpisode.new(episode_element)
  end

  it 'returns an empty string' do
    expect(@rss_feed_episode.content('title')).to eq('')
  end
end

There’s a lot of noise in the contents variable (e.g. <?xml version="1.0" encoding="UTF-8"?>). All that stuff is irrelevant to what the test is actually supposed to be testing. All this test should really care about is that we have an empty set of tags.

Here’s a refactored version of the same test:

context 'the element does not exist' do
  let(:rss_feed_episode) do 
    RSSFeedEpisodeTestFactory.create("<item></item>")
  end

  it 'returns an empty string' do
    expect(rss_feed_episode.content('title')).to eq('')
  end
end

Hopefully this is much more clear. The gory details of how to bring an RSS feed episode into existence are abstracted away into RSSFeedEpisodeTestFactory, a new class I created. Here’s what that class looks like:

class RSSFeedEpisodeTestFactory
  def self.create(inner_contents)
    @inner_contents = inner_contents
    rss_feed_episode
  end

  def self.rss_feed_episode
    RSSFeedEpisode.new(xml_doc.xpath('//item').first)
  end

  def self.xml_doc
    Nokogiri::XML(contents)
  end

  def self.contents
    %(
      <?xml version="1.0" encoding="UTF-8"?>
      <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
        <channel>#{@inner_contents}</channel>
      </rss>
    )
  end
end

Now I can use this factory class wherever I like. It not only helps me keep my tests more understandable but also helps cut down on duplication.

In the video below you can watch me refactor the “obscure” version into the more readable version as part of one of my free live Rails testing workshops.

Leave a Reply

Your email address will not be published. Required fields are marked *