Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSpec's mock framework does not work with steps when in before :each #6

Open
egilburg opened this issue Feb 19, 2012 · 4 comments
Open

Comments

@egilburg
Copy link

I have an example e.g.:

before :each do
  mock :something
end

steps "do something"
  it "should do 1" { assert_something }
  it "should do 2 { assert_something }
end

Throughout execution of the "do something" steps (which used to be a single long "it") I'm depending on the mock to have been done. But it doesn't work:

undefined method `mock' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_1:0x0000010248f2a8>

I tried rewriting the example as such:

steps "do something"
  before :each do
    mock :something
  end

  it "should do 1" { assert_something }
  it "should do 2 { assert_something }
end

The result, and error message, is the same. Now, if I simply ditch the before :each and do the following:

steps "do something"
  it "should do 1" { mock(:something); assert_something }
  it "should do 2 { mock(:something); assert_something }
end

The above DOES work, but it's very unDRY as I have many "it" statements. Ideally I'd want my first version, as my mock in question is generic (mocks the logged in user) and I really want it to simply work for every section in every steps.

I tried googling and found this:

http://www.ruby-forum.com/topic/217974

It seems that mock() doesn't work in "before :all", and from what I can guess, "steps" somehow implicitly converts a "before :each" to a "before :all"

I don't really care if "before :each" would run again prior to each "it" block, or only once per "steps" (but correctly making the mockspace objects available throughout each "it" block of the steps) since my mock statement is idempotent (but some people might care, e.g. if they create real records inside a before each). But from my needs, I just want to run the mock statement once and know that the result mocks are working within each "it" statement of the "steps"

Is it possible to achieve?

Thanks

@LRDesign
Copy link
Collaborator

Yes, rspec-steps treat's all before() blocks as before(:all). This is because the purpose of rspec-steps is to run a sequence of steps in the same environment. Environment variables are not erased and transactions are not rolled back between steps, so it doesn't make sense to do something before every example. They are effectively treated as one single story with a shared environment.

I'm not sure if mocks will do what you want here. We developed rspec-steps is for integration testing, and in that case we want to be testing against real data in the database. Mocks would defeat the purpose of that. So we exclusively use factories when doing integration testing; mocks only get used in controller tests.

However, a couple of suggestions to try:

  1. Try putting your mock in a let() or let!() block rather than a before() block, and see if that helps.

  2. Try creating your mocks inside the first step:

steps "whatever", :type => :request do
it "when my mocks are created" do
@my_mock = mock(:something)
end

it "should do something" do
... do something in browser ...
end

it "should show the right result" do
... assert something ...
end

end

@egilburg
Copy link
Author

  1. We mock the effects on other objects (using Class.any_instance), not those we directly call from tests, so it won't quite work (e.g. I mock that, when a controller receives method call "current_user", it would return something, whereas my request spec deals with other areas). True, they are "integration" in a sense (e.g. higher level than unit specs), but our architecture uses different gems (mounted as engines) for different areas, and we occasionally want an intra-gem integration test that covers all areas of that gem, without necessarily dragging in all the other gems.
  2. Tried it, their effects are localized only to their own "it" step, and then lost on subsequent "it" steps.

Thanks for your help anyways,

Eugene


From: Logical Reality Design [[email protected]]
Sent: Sunday, February 19, 2012 22:58
To: Gilburg, Eugene
Subject: Re: [rspec-steps] RSpec's mock framework does not work with steps when in before :each (#6)

Yes, rspec-steps treat's all before() blocks as before(:all). This is because the purpose of rspec-steps is to run a sequence of steps in the same environment - the main function of rspec-steps is to keep the environment alive as you go from example to example. Environment variables are not erased and transactions are not rolled back between steps, so it doesn't make sense to do something before every example.

I'm haven't ever used mocks in this environment. We developed rspec-steps is for integration testing, and in that case we want to be testing against real data in the database. Mocks would defeat the purpose of that. So we exclusively use factories when doing integration testing; mocks only get used in controller tests. So I'm not certain mocks will ever do what you want here. However, a couple of suggestions to try:

  1. Try putting your mock in a let() or let!() block rather than a before() block, and see if that helps.

  2. Try creating your mocks inside the first step:

steps "whatever", :type => :request do
it "when my mocks are created" do
@my_mock = mock(:something)
end

it "should do something" do
... do something in browser ...
end

it "should show the right result" do
... assert something ...
end

end


Reply to this email directly or view it on GitHub:
#6 (comment)

@nyarly
Copy link
Member

nyarly commented Feb 21, 2012

Using mocks with rspec-steps isn't, on the other hand, something we're not interested in.

You are in fact correct: before blocks in rspec-steps are "promoted" to before :all (there's even a warning to this effect) because running code between steps is problematic.

It's possible that the MockSpace could be likewise promoted to being checked and cleared in the before :all space - it's unlikely this has enough value to be worth implementing, though. But, maybe stubbing would do what you want?

Session.stub!(:current_user => User.new(:name => "I'm a mock!"))

Specifically: do you care that current user gets called, or are you just trying to remove a dependency on outside code.

@nyarly
Copy link
Member

nyarly commented Dec 10, 2015

@egilburg I know this is an ancient issue, but we recently released a new major version that should be more compatible with RSpec, and I think should address this issue. If you haven't moved completely on, could you give the newest version a try?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants