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
Fix teardown callbacks #50915
Fix teardown callbacks #50915
Conversation
… setup and teardown minitest lifecycle hooks. Minitest provides `setup` and `teardown` lifecycle hooks to run code in. In general it is best practice to when defining your own test case class, to use `Minitest::TestCase.setup` and `Minitest::TestCase.teardown` instead of `before_setup` and `after_teardown`. Per Minitest's Documentation on Lifecycle Hooks: https://docs.ruby-lang.org/en/2.1.0/MiniTest/Unit/LifecycleHooks.html > before_setup() > Runs before every test, before setup. This hook is meant for libraries to extend minitest. It is not meant to be used by test developers. > after_teardown() > Runs after every test, after teardown. This hook is meant for libraries to extend minitest. It is not meant to be used by test developers. Since the `setup` and `teardown` ActiveSupport::TestCase callbacks are in essence user code, it makes sense to run during their corresponding Minitest Lifecycle hooks.
…own code. By not adding wrapping the `teardown_fixtures` code, its possible that super raises an error and prevents the existing database transaction from rolling back. `super` in general should only be calling `Minitest::Testcase.after_teardown` however, if another library were to override `Minitest::Testcase.after_teardown`, like the popular gem [rspec-mocks](https://github.com/rspec/rspec-mocks/blob/main/lib/rspec/mocks/minitest_integration.rb#L23) does, it causes all subsequent tests to retain any changes that were made to the database in the original test that errors.
We actually do rescue any exceptions rails/activesupport/lib/active_support/testing/setup_and_teardown.rb Lines 47 to 49 in 979c6b0
This was fixed some time ago in #32472 , maybe you are using a old Rails version? I tried with your reproduction script in a new app and I couldn't reproduce the problem |
@Edouard-chin here is the specifications I'm using to run the test case I posted above:
I have just tested the code you linked in a require 'minitest'
begin
raise Minitest::Assertion, "assertion failed"
rescue => ex
puts "Exception #{ex} was caught"
end The results:
The exception is not caught and is raised out. I believe this is because:
|
@@ -36,12 +36,12 @@ def teardown(*args, &block) | |||
end | |||
end | |||
|
|||
def before_setup # :nodoc: | |||
def setup # :nodoc: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change can't be done. It is breaking a lot of tests, as you can see for all the files you had to change. The commit message for this change also is wrong. It say we can't use those hooks, but the hook documentation says clearly: This hook is meant for libraries to extend minitest.
We are a library and we are extending minitest
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the review! I think you're right, I didn't realize how much of a breaking change that would be.
I believe I have a more elegant solution that shouldn't break anything that is ready for review!
There are two test thread failures in actionpack
that seem to be flakey tests, and not related to my changes. I have not been able to reproduce locally, but I also cannot retry the step in Buildkite
This reverts commit f30682e.
This reverts commit 1d5b88c.
This reverts commit 60e89bd.
This reverts commit 0f19bc3.
This reverts commit e5673f1.
…o run in setup and teardown minitest lifecycle hooks." This reverts commit d08d92d.
… callback code to ensure all other after_teardown methods are called.
Thanks, you are right, I missed that. |
I have reverted the changes such that The new changes are:
|
Looking into the
I have found this run in Unless someone believes otherwise, I will consider this failure not related to my changes. |
* Switch ActiveSupport::TestCase teardown and setup callbacks to run in setup and teardown minitest lifecycle hooks. Minitest provides `setup` and `teardown` lifecycle hooks to run code in. In general it is best practice to when defining your own test case class, to use `Minitest::TestCase.setup` and `Minitest::TestCase.teardown` instead of `before_setup` and `after_teardown`. Per Minitest's Documentation on Lifecycle Hooks: https://docs.ruby-lang.org/en/2.1.0/MiniTest/Unit/LifecycleHooks.html > before_setup() > Runs before every test, before setup. This hook is meant for libraries to extend minitest. It is not meant to be used by test developers. > after_teardown() > Runs after every test, after teardown. This hook is meant for libraries to extend minitest. It is not meant to be used by test developers. Since the `setup` and `teardown` ActiveSupport::TestCase callbacks are in essence user code, it makes sense to run during their corresponding Minitest Lifecycle hooks. * Ensure test fixutres are torndown on errors in superclass after_teardown code. By not adding wrapping the `teardown_fixtures` code, its possible that super raises an error and prevents the existing database transaction from rolling back. `super` in general should only be calling `Minitest::Testcase.after_teardown` however, if another library were to override `Minitest::Testcase.after_teardown`, like the popular gem [rspec-mocks](https://github.com/rspec/rspec-mocks/blob/main/lib/rspec/mocks/minitest_integration.rb#L23) does, it causes all subsequent tests to retain any changes that were made to the database in the original test that errors. * Remove unnecessary setup and teardown methods in tests * update activesupport Changelog * Fix linter issues in CHANGELOG * fix tests with improper setup and teardown method definitions * Fix final CHANGELOG lint * Revert "Fix final CHANGELOG lint" This reverts commit f30682e. * Revert "fix tests with improper setup and teardown method definitions" This reverts commit 1d5b88c. * Revert "Fix linter issues in CHANGELOG" This reverts commit 60e89bd. * Revert "update activesupport Changelog" This reverts commit 0f19bc3. * Revert "Remove unnecessary setup and teardown methods in tests" This reverts commit e5673f1. * Revert "Switch ActiveSupport::TestCase teardown and setup callbacks to run in setup and teardown minitest lifecycle hooks." This reverts commit d08d92d. * Rescue Minitest::Assertion errors in ActiveSupport::TestCase.teardown callback code to ensure all other after_teardown methods are called. * Fix name of test class * remove unused MyError class * Fix module to not be in global namespace Co-authored-by: Rafael Mendonça França <rafael@rubyonrails.org>
@rafaelfranca @Edouard-chin Thank you both for the reviews and getting this merged upstream! |
* Switch ActiveSupport::TestCase teardown and setup callbacks to run in setup and teardown minitest lifecycle hooks. Minitest provides `setup` and `teardown` lifecycle hooks to run code in. In general it is best practice to when defining your own test case class, to use `Minitest::TestCase.setup` and `Minitest::TestCase.teardown` instead of `before_setup` and `after_teardown`. Per Minitest's Documentation on Lifecycle Hooks: https://docs.ruby-lang.org/en/2.1.0/MiniTest/Unit/LifecycleHooks.html > before_setup() > Runs before every test, before setup. This hook is meant for libraries to extend minitest. It is not meant to be used by test developers. > after_teardown() > Runs after every test, after teardown. This hook is meant for libraries to extend minitest. It is not meant to be used by test developers. Since the `setup` and `teardown` ActiveSupport::TestCase callbacks are in essence user code, it makes sense to run during their corresponding Minitest Lifecycle hooks. * Ensure test fixutres are torndown on errors in superclass after_teardown code. By not adding wrapping the `teardown_fixtures` code, its possible that super raises an error and prevents the existing database transaction from rolling back. `super` in general should only be calling `Minitest::Testcase.after_teardown` however, if another library were to override `Minitest::Testcase.after_teardown`, like the popular gem [rspec-mocks](https://github.com/rspec/rspec-mocks/blob/main/lib/rspec/mocks/minitest_integration.rb#L23) does, it causes all subsequent tests to retain any changes that were made to the database in the original test that errors. * Remove unnecessary setup and teardown methods in tests * update activesupport Changelog * Fix linter issues in CHANGELOG * fix tests with improper setup and teardown method definitions * Fix final CHANGELOG lint * Revert "Fix final CHANGELOG lint" This reverts commit f30682e. * Revert "fix tests with improper setup and teardown method definitions" This reverts commit 1d5b88c. * Revert "Fix linter issues in CHANGELOG" This reverts commit 60e89bd. * Revert "update activesupport Changelog" This reverts commit 0f19bc3. * Revert "Remove unnecessary setup and teardown methods in tests" This reverts commit e5673f1. * Revert "Switch ActiveSupport::TestCase teardown and setup callbacks to run in setup and teardown minitest lifecycle hooks." This reverts commit d08d92d. * Rescue Minitest::Assertion errors in ActiveSupport::TestCase.teardown callback code to ensure all other after_teardown methods are called. * Fix name of test class * remove unused MyError class * Fix module to not be in global namespace Co-authored-by: Rafael Mendonça França <rafael@rubyonrails.org>
* Switch ActiveSupport::TestCase teardown and setup callbacks to run in setup and teardown minitest lifecycle hooks. Minitest provides `setup` and `teardown` lifecycle hooks to run code in. In general it is best practice to when defining your own test case class, to use `Minitest::TestCase.setup` and `Minitest::TestCase.teardown` instead of `before_setup` and `after_teardown`. Per Minitest's Documentation on Lifecycle Hooks: https://docs.ruby-lang.org/en/2.1.0/MiniTest/Unit/LifecycleHooks.html > before_setup() > Runs before every test, before setup. This hook is meant for libraries to extend minitest. It is not meant to be used by test developers. > after_teardown() > Runs after every test, after teardown. This hook is meant for libraries to extend minitest. It is not meant to be used by test developers. Since the `setup` and `teardown` ActiveSupport::TestCase callbacks are in essence user code, it makes sense to run during their corresponding Minitest Lifecycle hooks. * Ensure test fixutres are torndown on errors in superclass after_teardown code. By not adding wrapping the `teardown_fixtures` code, its possible that super raises an error and prevents the existing database transaction from rolling back. `super` in general should only be calling `Minitest::Testcase.after_teardown` however, if another library were to override `Minitest::Testcase.after_teardown`, like the popular gem [rspec-mocks](https://github.com/rspec/rspec-mocks/blob/main/lib/rspec/mocks/minitest_integration.rb#L23) does, it causes all subsequent tests to retain any changes that were made to the database in the original test that errors. * Remove unnecessary setup and teardown methods in tests * update activesupport Changelog * Fix linter issues in CHANGELOG * fix tests with improper setup and teardown method definitions * Fix final CHANGELOG lint * Revert "Fix final CHANGELOG lint" This reverts commit f30682e. * Revert "fix tests with improper setup and teardown method definitions" This reverts commit 1d5b88c. * Revert "Fix linter issues in CHANGELOG" This reverts commit 60e89bd. * Revert "update activesupport Changelog" This reverts commit 0f19bc3. * Revert "Remove unnecessary setup and teardown methods in tests" This reverts commit e5673f1. * Revert "Switch ActiveSupport::TestCase teardown and setup callbacks to run in setup and teardown minitest lifecycle hooks." This reverts commit d08d92d. * Rescue Minitest::Assertion errors in ActiveSupport::TestCase.teardown callback code to ensure all other after_teardown methods are called. * Fix name of test class * remove unused MyError class * Fix module to not be in global namespace Co-authored-by: Rafael Mendonça França <rafael@rubyonrails.org>
Motivation / Background
This Pull Request has been created because
ActiveRecord::TestFixtures.teardown_fixtures
method does not get called if aMiniTest::Assertion
error is raised inActiveSupport::TestCase.teardown
callbacks.Test Case to Reproduce the Problem
Running the above test case with a seed that runs
should fail
and thenshould pass
:Running the test file with a seed with the following order:
should pass
->should fail
:Specifications
ruby: 3.2.2
rails: 7.1.1
minitest: 5.21.0
OS: MacOS arm64-darwin
Database: SQLite
Detail
This Pull Request changes the following files:
ActiveSupport::Testing::SetupAndTeardown
Renames the following class methods in
after_teardown
->teardown
before_setup
->setup
.Why switch from
after_teardown
toteardown
andbefore_setup
tosetup
?When a user defines a
teardown
callback block in their test case, it will actually run in theafter_teardown
Minitest Lifecycle hook. This is confusing and has a major side effect that if a user defines anassertion
in that teardown code, it will raise out without getting rescued (see the calling code). This meanssuper
will never get called,ActiveRecord::TestFixtures.teardown_fixtures
will never run, and the database state will not get rolled back.By moving the
teardown
callbacks to run in theteardown
lifecycle, allafter_teardown
code will run since the error will get rescued by Minitest's lifecycle management:Minitest also provides recommendations about when to use
after_teardown
andbefore_setup
:https://docs.seattlerb.org/minitest/Minitest/Test/LifecycleHooks.html
There is a slight grey area here, where the
setup
andteardown
callbacks are code written and provided by the "test developers". It was surprising at least to me to find that they were not run insetup
andteardown
Minitest Lifecycle hooks but ratherbefore_setup
andafter_teardown
.Moving the callbacks to run in their respective Minitest Lifecycle hooks properly will follow Minitest's recommendations and clear up any confusion about where these callbacks are run.
ActiveRecord::TestFixture.after_teardown
teardown_fixtures
inensure
block to always run the code that rolls back the test's database transactions.Checklist
Before submitting the PR make sure the following are checked:
[Fix #issue-number]