Stubbing not working at all with dry-rails

We’re on dry-rails 0.7.0 and I tried adding

require "dry/container/stub"

MyApp::Container.enable_stubs!

To our rails_helper and I just get a NoMethodError:

Failure/Error: MyApp::Container.enable_stubs!

NoMethodError:
  undefined method `enable_stubs!' for MyApp::Container:Class
  Did you mean?  enabled_plugins

We’re on Rails 6.0 and Ruby 3.0 and using rspec. I can just stub things with rspec native stubbing, and I’m not sure why I shouldn’t…? eg

allow(MyApp::Container).to receive(:[]).with("something").and_return(
  "stubbed value"
)

OK it’s a bit wordy but does the job for me for now… it would be nice if native dry.rb stubbing worked though. I imagine that if I were testing something more complicated, this approach would not work so well

This is because dry-rails is providing a Dry::System::Container, not a Dry::Core::Container directly. A Dry::System container is a wrapper that provides additional functionality on top of Dry::Core::Container.

Here’s how I setup container testing:

module RSpec::Testing
  module StubHelpers
    def container_stub(key, &block)
      around :example do |example|
        MyApp::Container.stub(key, instance_exec(&block)) { example.run }
      end
    end
  end
end

require "dry/system/stubs"
MyApp::Container.enable_stubs!

RSpec.configure do |config|
  config.extend RSpec::Testing::StubHelpers
end

And within a spec:

RSpec.describe "My Thing" do
  let(:something) { Thing.new }
  container_stub("my.thing") { something }
end

I also want to clarify that I don’t use stubs in all situations. If your thing under test uses autoinject dependencies, you can just pass them in as arguments:

let(:repo) { FakeRepo.new }
subject(:thing) { Thing.new(repo:) }

Container stubbing is useful for transitive dependencies; setting up dependencies of dependencies is where argument-passing becomes really onerous.

So I tend to use this technique for integration testing mostly.