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

Testing code with at_exit conflicts with raise_error #1117

Closed
edisonywh opened this issue Jun 7, 2019 · 7 comments
Closed

Testing code with at_exit conflicts with raise_error #1117

edisonywh opened this issue Jun 7, 2019 · 7 comments

Comments

@edisonywh
Copy link

Hey guys! I currently have a class like this:

module Gem
  def custom_method
    at_exit do
      # code_logic
      raise StandardError
    end
  end
end

The reason I am wrapping my code inside at_exit is because I want to run the execution of the code AFTER my base class has finished defining the methods. For example:

class Foo
  extend Gem
  custom_method
  def method_one; end
end

I want to access method_one from inside my gem, I have two ways to do it:

  1. Put my custom_method all the way at the bottom of the file
  2. Wrap my custom_method around an END block or an at_exit block, both of which will ensure that my custom_method runs after the file that extended the file.

I've decided to go with the second option because I do not want to put the method call at the end of the file.

It all works fine when I test it manually, however, the issue is as follow when I try to test it with rspec.

I want to test my method like so:

expect do
  Foo = Class.new do
    extend Gem
    custom_method
    def method_one; end
  end
end.to raise_error StandardError

But this won't work, rspec will think that there's no error, I presume this is because my at_exit runs after the rspec raise_error, which means the error is raised after the expectation.

How can I make my test green? Appreciate any pointers!

@JonRowe JonRowe transferred this issue from rspec/rspec-metagem Jun 7, 2019
@edisonywh
Copy link
Author

For a more concrete example, I just published the gem in question, so you can dig into the source code too.

https://github.com/edisonywh/behaves

@JonRowe
Copy link
Member

JonRowe commented Jun 8, 2019

👋 This is a tough one, your test is working correctly, because at_exit hooks are installed at... well.. exit, by which time your test suite has already failed. The way to test this is, probably, to assert that at_exit is received with a stub and then check the block raises StandardError when invoked.

As far as I know there is no way to intercept the blocks and check them, and in any case would cause RSpec to exit uncleanly.

@JonRowe JonRowe closed this as completed Jun 8, 2019
edisonywh added a commit to edisonywh/behaves that referenced this issue Jun 12, 2019
A reply is given for the [RSpec
issue](rspec/rspec-expectations#1117), I've
reviewed the proposed solution and found that I'd take a different
approach instead in order to fix the `at_exit` spec.

First off, this commit extracts out the core logic for `behaves_like`
into a `private` method. This is important for a couple of reason.

By extracting it out, `behaves_like` now only consists of the `at_exit`
logic, and because I can't test `at_exit` due to the nature of it, I've
decided to test directly on the newly extracted `private` method.

While it is generally frowned upon to test private methods (you should
be testing the public interfaces only!!), and I do agree on that on a
general case, I think that this is an exceptional situation that calls
for a break of rules.

Guidelines should be all that is, guides, it should not be rules that
prevent innovation. Thus I've decided to break the rule this time.

Fixes #2
@edisonywh
Copy link
Author

Cool, thanks for confirming what I suspected!

With that, I think I'm going to break the rules of no testing private method, as I don't think it should be a hard rule but rather a guideline, and one that's reasonable to break in my case.

My fix is over at edisonywh/behaves#16 along with my rationale, if someone is interested.

Thanks for taking the time to answer my question, Jon!

@JonRowe
Copy link
Member

JonRowe commented Jun 12, 2019

You could also test it by shelling out and running your code that way, which is how we test some parts of RSpec.

@edisonywh
Copy link
Author

I don't understand what you mean by shelling out, could you explain what's that? Thanks!

@JonRowe
Copy link
Member

JonRowe commented Jun 13, 2019

Running your code as a seperate process on the command line and testing the resulting output.

@JonRowe
Copy link
Member

JonRowe commented Jun 13, 2019

You could also experiment with fork a quick check says at_exit is run, but not rescuable but again you might be able to assert on the output

fancy517 pushed a commit to fancy517/behaves that referenced this issue Oct 5, 2023
A reply is given for the [RSpec
issue](rspec/rspec-expectations#1117), I've
reviewed the proposed solution and found that I'd take a different
approach instead in order to fix the `at_exit` spec.

First off, this commit extracts out the core logic for `behaves_like`
into a `private` method. This is important for a couple of reason.

By extracting it out, `behaves_like` now only consists of the `at_exit`
logic, and because I can't test `at_exit` due to the nature of it, I've
decided to test directly on the newly extracted `private` method.

While it is generally frowned upon to test private methods (you should
be testing the public interfaces only!!), and I do agree on that on a
general case, I think that this is an exceptional situation that calls
for a break of rules.

Guidelines should be all that is, guides, it should not be rules that
prevent innovation. Thus I've decided to break the rule this time.

Fixes #2
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