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

Generated test environment config prevents classes from being partially doubled in tests #721

Closed
3 tasks done
greveritt opened this issue Apr 25, 2024 · 2 comments
Closed
3 tasks done
Labels

Comments

@greveritt
Copy link
Contributor

greveritt commented Apr 25, 2024

Checklist

  • Upgrade Jets: Are you using the latest version of Jets? This allows Jets to fix issues fast. There's a jets upgrade command that makes this a simple task. There's also an Upgrading Guide: http://rubyonjets.com/docs/upgrading/
  • Reproducibility: Are you reporting a bug others will be able to reproduce and not asking a question. If you're unsure or want to ask a question, do so on https://community.boltops.com
  • Code sample: Have you put together a code sample to reproduce the issue and make it available? Code samples help speed up fixes dramatically. If it's an easily reproducible issue, then code samples are not needed. If you're unsure, please include a code sample.

My Environment

Software Version
macOS 14.4.1 (23E224)
Jets 5.0.12
Ruby 3.2.3

Expected Behaviour

The following RSpec code prevents the actual implementation of ClassName#method_name from being run in the test example.

allow(ClassName).to receive(:method_name).and_raise 'Whoopsy daisy!'

Current Behavior

The original implementation of ClassName#method_name is run.

Step-by-step reproduction instructions

I created an example application that demonstrates the issue. I may have experienced this bug more consistently in controller tests, so the example app partially mocks a non-controller class inside of a controller spec.

  1. Clone the git repository linked to below.
  2. Make sure you're on branch main.
  3. Run bundle exec rspec spec/controllers/demo_controller_spec.rb on the command line. NOTE: You can write a similar spec in your own test application by creating a class outside of the Jets class hierarchy, mocking a class method on that class, and writing an expectation for the mocked behavior.

You will see test output similar to this:

Failing test output
DemoController
  GET #index
    when the utility class is not stubbed
      executes the original method
    when the utility class is partially stubbed to raise an error
      rescues the error and returns a 500 status (FAILED - 1)

Failures:

  1) DemoController GET #index when the utility class is partially stubbed to raise an error rescues the error and returns a 500 status
     Failure/Error: expect { get '/demo' }.to raise_error 'Whoopsy daisy!'
       expected Exception with "Whoopsy daisy!" but nothing was raised
     # ./spec/controllers/demo_controller_spec.rb:18:in `block (4 levels) in <top (required)>'

Finished in 0.03077 seconds (files took 0.93326 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./spec/controllers/demo_controller_spec.rb:17 # DemoController GET #index when the utility class is partially stubbed to raise an error rescues the error and returns a 500 status

How to write your own test application

  1. Create a new API app like so: jets new class-caching-error-demo --mode api
  2. Write a new non-controller class outside of the Jets hierarchy in a file such as app/utilities/demo_utility.rb. This class will be referred to as "the utility class" from here on out.
  3. Call a method of the utility class in a controller action.
  4. Partially mock a class method of the utility class in the controller spec.
  5. Write a test example that expects the mocked behavior and place it in the same scope as the partial mock.

Code Sample

Git repository of test application

Solution Suggestion

Change the template for the test application environment so that configuration option cache_classes is set to true, not false. Setting this option to false makes it so that the partially mocked class is reloaded without the mock when it's used in the application code under test.

The Jets default is also the opposite of the Rails default. In Rails, cache_classes is set to true in testing environments unless Spring is installed. Generated Jets apps do not come with Spring. Please refer to the documentation of config.cache_classes in Rails 7.0. I have chosen to refer to the Rails 7.0 manual because Jets 5.0.12 includes libraries from Rails 7.0.8.1.

The test app's Git repo has a branch called fix-test-config, which has cache_classes set to true, contrary to the Jets default. The tests in demo_controller_spec.rb pass on that branch:

Output of tests succeeding with fixed test environment configuration
DemoController
  GET #index
    when the utility class is not stubbed
      executes the original method
    when the utility class is partially stubbed to raise an error
      rescues the error and returns a 500 status

Finished in 0.01534 seconds (files took 0.87718 seconds to load)
2 examples, 0 failures
@greveritt greveritt added the bug label Apr 25, 2024
greveritt added a commit to greveritt/jets that referenced this issue Apr 25, 2024
This default allows users to make partial doubles of classes in app/.
This fix addresses issue rubyonjets#721.
@greveritt
Copy link
Contributor Author

@tongueroo Was there a specific reason why cache_classes was set to false by default? I haven't experienced any ill effects from enabling it in my project. I've only experienced the benefits of partial class doubles working again. Please let me know if there is anything I can do to improve the pull request that I created: #722.

@greveritt
Copy link
Contributor Author

This was resolved by #722. Thanks, @tongueroo!

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

No branches or pull requests

1 participant