thumblemonks / riot

Riot is a fast, expressive, and contextual ruby unit testing framework
http://thumblemonks.github.com/riot/
MIT License
314 stars 28 forks source link

setups get run multiple times, big issue with RR #31

Closed DouglasMeyer closed 12 years ago

DouglasMeyer commented 13 years ago

Hey Guys-

I ran into this issue when I was using a RR mock in a parent context's setup (see example).

require '.....'
context "bla" do
  setup do
    puts "SETUP"
    Object.new.tap do |obj
      mock(obj).something{ true }
    end
  end
  context "a context" do
    should :something
  end
end

Output:

SETUP
bla a context
SETUP
  - should something: something() Called 0 times. Expected 1 times.

It looks like the setup is getting called for the bla context, then once again for the a context context. I would guess a way to fix this is to not have the contexts run the setups and teardowns, but have the "runnables" run them.

Thanks, -Doug

achiurizo commented 13 years ago

The parent context setup are ran again within each child context. So your a context context is going to setup the mocks again. One way you can avoid this is to do the mocking within the assertion that your testing, or not nest the context that you don't want the mocking to be also ran on.

DouglasMeyer commented 13 years ago

You may be right in the case of the example, but it is still an issue that riot needlessly runs setup/teardown multiple times. This issue is easily missed unless the setup/teardown takes a while or you use RR.

toothrot commented 13 years ago

This is actually intentional: here is an example from an old discussion

If you think about it, each context is sort of its own world. The sub-context has to re-run them since the previous context will have cleaned up after itself in its teardowns, ideally.

DouglasMeyer commented 13 years ago

Ok, I think I have a good idea of the model. So it looks like this isn't so much an issue with contexts as it is how RR is used. As this example should show, if there aren't any assertions in the parent context, the expectations from the setup are never reset.

gus commented 12 years ago

Hey Doug,

Sorry for the much belated response. I agree with you that there is a problem. The tests should pass whether you comment out the first should :get_mocked_once or not.

What's strange is that if I do this, it works:

require 'rubygems'
require 'riot'
require 'riot/rr'

context "riot sanity test" do
  #setup do
    #puts "SETUP"
    #Object.new.tap do |obj|
      #mock(obj).get_mocked_once{ true }
    #end
  #end

  #un-comment this next line and the tests magically pass
  #should :get_mocked_once
  context "generic context" do
    setup do
      puts "SETUP"
      Object.new.tap do |obj|
        mock(obj).get_mocked_once{ true }
      end
    end

    should "call get mocked once" do 
      topic.get_mocked_once
    end
  end
end

Taking a look.

gus commented 12 years ago

Even "funnier" is this:

require 'rubygems'
require 'riot'
require 'riot/rr'

context "riot sanity test" do
  setup do
    puts "SETUP"
    Object.new.tap do |obj|
      mock(obj).get_mocked_once{ puts "here"; true }
      puts obj.inspect
    end
  end

  #should :get_mocked_once

  context "with subcontext" do
    should "x" do
      topic.get_mocked_once
      topic.get_mocked_once
    end
  end
end

Which generates this message:

situation/on
SETUP
riot sanity test with subcontext
SETUP
  ! should x: get_mocked_once()
Called 2 times.
Expected 1 times.

0 passes, 0 failures, 1 errors in 0.005137 seconds

So, in this case it sees that's it been called two times. Quite fishy indeed.

DouglasMeyer commented 12 years ago

Thanks for looking into this inconsistency.