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

Nested Before/Around Run In Weird Order #2544

Closed
benmmurphy opened this issue May 22, 2018 · 6 comments · Fixed by #2661
Closed

Nested Before/Around Run In Weird Order #2544

benmmurphy opened this issue May 22, 2018 · 6 comments · Fixed by #2661

Comments

@benmmurphy
Copy link

Subject of the issue

If you have a describe block with an before(:each) and then nest a describe block with an around(:each) the around(:each) runs before the before(:each). I would have thought the around(:each) is just a nice way of writing before(:each) after(:each) and sharing some state between them. but because of the way around(:each) always runs before before(:each) even if it is nested lower it makes around(:each) much less useful.

there doesn't seem to be a test case for this behaviour in the documentation so I'm not sure if this was deliberate or not. though, i guess it would be hard to change this behaviour because probably a lot of people rely on it.

https://relishapp.com/rspec/rspec-core/v/2-0/docs/hooks/around-hooks#before/after(:each)-hooks-are-wrapped-by-the-around-hook

Your environment

  • Ruby version: 2.4.0
  • rspec-core version: 3.7

Steps to reproduce

describe "filter" do

  before(:each) do
    puts "before each outer"
  end

  describe "inner" do
    around(:each) do |example|
      puts "around each inner"
      example.run
    end

    it "should" do
      puts "example"
    end
  end

end

actual:

around each inner
before each outer
example

expected:

before each outer
around each inner
example

here is how it works if they are both before(:each) rather than using an around(:each) for the inner.

describe "filter" do

  before(:each) do
    puts "before each outer"
  end

  describe "inner" do
    before(:each) do
      puts "before each inner"
    end

    it "should" do
      puts "example"
    end
  end

end
before each outer
before each inner
example
@JonRowe
Copy link
Member

JonRowe commented May 24, 2018

Verified this is a bit odd, we should at least document the order that we expect this to run in, I think at least around hooks should run after before when defined in an inner context

@deivid-rodriguez
Copy link
Contributor

So, I think this would be a good fix for RSpec 4? It's a bit odd, indeed!

@deivid-rodriguez
Copy link
Contributor

I'd like to give this a try, would you be willing to accept a PR for RSpec 4?

@JonRowe
Copy link
Member

JonRowe commented Sep 30, 2018

Yes, for RSpec 4, I would recommend developing it as in an opt-in feature, and we will eventually need a way to warn about the change in behaviour.

@deivid-rodriguez
Copy link
Contributor

Sounds like a good plan to me! I'll see if I can find some time to work on it!

deivid-rodriguez added a commit to rubygems/bundler that referenced this issue May 24, 2019
Sometimes, the initial environment before the first spec would not be
properly reset, causing some specs to fail if they run first.

Reproducible with

```
bin/rspec \
  --seed 52285 \
  -e "Bundler.unbundled_env behaves like an unbundling helper should clean up RUBYLIB"
```

This change causes a separate spec failure, namely:

```
$ bin/rspec spec/bundler/gem_helper_spec.rb:294

1) Bundler::GemHelper gem management release:rubygem_push success messaging No allowed_push_host set RUBYGEMS_HOST env var is set should report successful push to the host from the environment
   Failure/Error: expect(Bundler.ui).to receive(:confirm).with(message)

      #<Bundler::UI::Shell:0x0000556bc98a8f60 @shell=#<Bundler::Thor::Shell::Color:0x0000556bc98e80e8 @base=nil, @mute=false, @padding=0, @always_force=false>, @Level="info", @warning_history=[]> received :confirm with unexpected arguments
       expected: ("Pushed lorem__ipsum 0.1.0 to https://custom.env.gemhost.com")
            got: ("Pushed lorem__ipsum 0.1.0 to rubygems.org")
   # ./spec/bundler/gem_helper_spec.rb:54:in `mock_confirm_message'
   # ./spec/bundler/gem_helper_spec.rb:286:in `block (6 levels) in <top (required)>'
   # ./spec/spec_helper.rb:100:in `block (2 levels) in <top (required)>'

```

This is due to some RSpec weirdness. In particular,
rspec/rspec-core#2544.

If you have an outer `before(:each)` block, and an inner `around(:each)`
block, it turns out that the inner `around(:each)` runs before the outer
`before(:each)`. This is very unintuitive and might change in future
RSpec's. In our case, it causes a particular failure where the `around`
hook would set a value for an environment variable, and then it would
be unintentionally reset by our global `before` hook.

We workaround the issue by changing the global `before` hook to an
`around` hook, so that the ordering respects nesting.
deivid-rodriguez added a commit to rubygems/bundler that referenced this issue May 27, 2019
Sometimes, the initial environment before the first spec would not be
properly reset, causing some specs to fail if they run first.

Reproducible with

```
bin/rspec \
  --seed 52285 \
  -e "Bundler.unbundled_env behaves like an unbundling helper should clean up RUBYLIB"
```

This change causes a separate spec failure, namely:

```
$ bin/rspec spec/bundler/gem_helper_spec.rb:294

1) Bundler::GemHelper gem management release:rubygem_push success messaging No allowed_push_host set RUBYGEMS_HOST env var is set should report successful push to the host from the environment
   Failure/Error: expect(Bundler.ui).to receive(:confirm).with(message)

      #<Bundler::UI::Shell:0x0000556bc98a8f60 @shell=#<Bundler::Thor::Shell::Color:0x0000556bc98e80e8 @base=nil, @mute=false, @padding=0, @always_force=false>, @Level="info", @warning_history=[]> received :confirm with unexpected arguments
       expected: ("Pushed lorem__ipsum 0.1.0 to https://custom.env.gemhost.com")
            got: ("Pushed lorem__ipsum 0.1.0 to rubygems.org")
   # ./spec/bundler/gem_helper_spec.rb:54:in `mock_confirm_message'
   # ./spec/bundler/gem_helper_spec.rb:286:in `block (6 levels) in <top (required)>'
   # ./spec/spec_helper.rb:100:in `block (2 levels) in <top (required)>'

```

This is due to some RSpec weirdness. In particular,
rspec/rspec-core#2544.

If you have an outer `before(:each)` block, and an inner `around(:each)`
block, it turns out that the inner `around(:each)` runs before the outer
`before(:each)`. This is very unintuitive and might change in future
RSpec's. In our case, it causes a particular failure where the `around`
hook would set a value for an environment variable, and then it would
be unintentionally reset by our global `before` hook.

We workaround the issue by changing the global `before` hook to an
`around` hook, so that the ordering respects nesting.
@JonRowe
Copy link
Member

JonRowe commented Sep 12, 2019

Closing for now as there have been additional docs added, potential reorder can be looked at later.

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

Successfully merging a pull request may close this issue.

3 participants