0

I am working on an application for a blog using Ruby on Rails. I have a model called Essay with a Draper Decorator. I am also using MiniTest::Spec for testing this application. Each Essay has a body which will be stored as Markdown. In the EssayDecorator, I have a method called body which renders the Markdown to html using RedCarpet.

In order to test this method, I wrote the following code:

describe '#body' do
  it 'returns html from the markdown' do
    essay = FactoryGirl.create(:essay)
    @decorated_essay = essay.decorate
    markdown = Minitest::Mock.new 

    @decorated_essay.stub :markdown, markdown do
      markdown.expect :render, "<p>Test</p>", [essay.body]
      @decorated_essay.send(:body)
      markdown.verify
    end
  end
end

And inside the decorator I have two methods:

  def body
    markdown.render(model.body).html_safe
  end

  def markdown
    Redcarpet::Markdown.new(Redcarpet::Render::HTML, :autolink => true, :space_after_headers => true)
  end

This test passes, but seems weird to me. I don't want to test that RedCarpet is doing its job, I just want to test that I call the render method.

Is there a best practice for mocking out this kind of thing in MiniTest? I am pretty new to using Mocks and very new to using MiniTest.

Thanks, in advance.

Alex Shenoy
  • 101
  • 1
  • 4
  • Have a look at [rr](https://github.com/rr/rr) for a more flexible test double library (pairs nicely with MiniTest), as well as a general discussion of how mocks, stubs, spies, and proxies can help you test your code. – Zach Kemp Sep 10 '13 at 02:05

1 Answers1

0

IMO this code seems weird because it is not testing the behavior of your code, but it is testing the implementation. If the implementation changed (you stored the HTML in a cache instead of running it through Redcarpet) then this test would fail. This looks over-mocked to me.

I don't want to test that RedCarpet is doing its job, I just want to test that I call the render method.

This is implementation. Why are you running the body through markdown? Because you want hyperlinks to be created from URLs? I'd create a test for that. You want to ensure links have the no-follow attribute? I'd create a test for that. Create tests for why the code is doing something, not how it is doing it.

It is also entirely possible you are missing an abstraction in your application. Something that is responsible for formatting the plain text into HTML. Something that uses Redcarpet or RDiscount or any other library deemed important. The EssayDecorator probably shouldn't be responsible for ensuring that the text was formatted properly, but it may be responsible for telling the right object to apply the formatting.

blowmage
  • 8,854
  • 2
  • 35
  • 40
  • Creating a test for hyperlinks being rendered properly seems like something that would live in an acceptance test. Not the test for this method. If I test that on the decorator, wouldn't the acceptance test be testing that functionality twice? – Alex Shenoy Sep 19 '13 at 13:38
  • This goes to the purpose of tests. In TDD they are there to give feedback on the API you are exposing. In this case, your domain models are exposing an API to your presentation layer. The tests describe the functionality, but not the implementation. This is important because you want to be able to refactor - to change the implementation without changing the API, and have your tests still pass. When you specify the implementation in tests, you are creating coupling. Coupling is just as bad in tests as it is in the rest of your code. So the question I'm left with is why are you mocking at all? – blowmage Sep 19 '13 at 14:20