Browse files

`config.mock_with` and `config.expect_with` yield custom config object

to a block if given

This supports decoupled config for other rspec and 3rd party libs.

    RSpec.configure do |c|
      c.expect_with ExpectationFramework do |other_framework_config|
        other_framework_config.custom_setting = true
      end
      c.mock_with OtherFramework do |other_framework_config|
        other_framework_config.custom_setting = true
      end
    end
  • Loading branch information...
1 parent b1b2ae6 commit 651d8d9b1eb2febafffbac3ae0be95b35123237d @dchelimsky dchelimsky committed May 8, 2012
Showing with 105 additions and 32 deletions.
  1. +8 −0 Changelog.md
  2. +51 −17 lib/rspec/core/configuration.rb
  3. +46 −15 spec/rspec/core/configuration_spec.rb
View
8 Changelog.md
@@ -1,3 +1,11 @@
+### dev
+
+Enhancements
+
+* `config.mock_with` and `config.expect_with` yield custom config object to a
+ block if given
+ * aids decoupling from rspec-core's configuation
+
### 2.10.0 / 2012-05-03
[full changelog](http://github.com/rspec/rspec-core/compare/v2.9.0...v2.10.0)
View
68 lib/rspec/core/configuration.rb
@@ -278,24 +278,31 @@ def mock_framework=(framework)
#
# `framework` can be a Symbol or a Module.
#
- # Given any of :rspec, :mocha, :flexmock, or :rr, configures the named
- # framework.
+ # Given any of `:rspec`, `:mocha`, `:flexmock`, or `:rr`, configures the
+ # named framework.
#
- # Given :nothing, configures no framework. Use this if you don't use any
- # mocking framework to save a little bit of overhead.
+ # Given `:nothing`, configures no framework. Use this if you don't use
+ # any mocking framework to save a little bit of overhead.
#
# Given a Module, includes that module in every example group. The module
# should adhere to RSpec's mock framework adapter API:
#
- # setup_mocks_for_rspec
- # - called before each example
+ # setup_mocks_for_rspec
+ # - called before each example
#
- # verify_mocks_for_rspec
- # - called after each example. Framework should raise an exception
- # when expectations fail
+ # verify_mocks_for_rspec
+ # - called after each example. Framework should raise an exception
+ # when expectations fail
#
- # teardown_mocks_for_rspec
- # - called after verify_mocks_for_rspec (even if there are errors)
+ # teardown_mocks_for_rspec
+ # - called after verify_mocks_for_rspec (even if there are errors)
+ #
+ # If the module responds to `configuration` and `mock_with` receives a block,
+ # it will yield the configuration object to the block e.g.
+ #
+ # config.mock_with OtherMockFrameworkAdapter do |mod_config|
+ # mod_config.custom_setting = true
+ # end
def mock_with(framework)
framework_module = case framework
when Module
@@ -324,6 +331,11 @@ def mock_with(framework)
assert_no_example_groups_defined(:mock_framework)
end
+ if block_given?
+ raise "#{framework_module} must respond to `configuration` so that expect_with can yield it." unless framework_module.respond_to?(:configuration)
+ yield framework_module.configuration
+ end
+
@mock_framework = framework_module
end
@@ -338,16 +350,33 @@ def expectation_framework=(framework)
expect_with(framework)
end
- # Sets the expectation framework module(s).
+ # Sets the expectation framework module(s) to be included in each example
+ # group.
+ #
+ # `frameworks` can be `:rspec`, `:stdlib`, a custom module, or any
+ # combination thereof:
+ #
+ # config.expect_with :rspec
+ # config.expect_with :stdlib
+ # config.expect_with :rspec, :stdlib
+ # config.expect_with OtherExpectationFramework
#
- # `frameworks` can be :rspec, :stdlib, or both
+ # RSpec will translate `:rspec` and `:stdlib` into the appropriate
+ # modules.
#
- # Given :rspec, configures rspec/expectations.
- # Given :stdlib, configures test/unit/assertions
- # Given both, configures both
+ # ## Configuration
+ #
+ # If the module responds to `configuration`, `expect_with` will
+ # yield the `configuration` object if given a block:
+ #
+ # config.expect_with OtherExpectationFramework do |custom_config|
+ # custom_config.custom_setting = true
+ # end
def expect_with(*frameworks)
modules = frameworks.map do |framework|
case framework
+ when Module
+ framework
when :rspec
require 'rspec/expectations'
self.expecting_with_rspec = true
@@ -364,7 +393,12 @@ def expect_with(*frameworks)
assert_no_example_groups_defined(:expect_with)
end
- @expectation_frameworks.clear
+ if block_given?
+ raise "expect_with only accepts a block with a single argument. Call expect_with #{modules.length} times, once with each argument, instead." if modules.length > 1
+ raise "#{modules.first} must respond to `configuration` so that expect_with can yield it." unless modules.first.respond_to?(:configuration)
+ yield modules.first.configuration
+ end
+
@expectation_frameworks.push(*modules)
end
View
61 spec/rspec/core/configuration_spec.rb
@@ -64,10 +64,37 @@ module RSpec::Core
end
end
+ shared_examples "a configurable framework adapter" do |m|
+ it "yields a config object if the framework_module supports it" do
+ custom_config = Struct.new(:custom_setting).new
+ mod = Module.new
+ mod.stub(:configuration => custom_config)
+
+ config.send m, mod do |mod_config|
+ mod_config.custom_setting = true
+ end
+
+ custom_config.custom_setting.should be_true
+ end
+
+ it "raises if framework module doesn't support configuration" do
+ mod = Module.new
+
+ lambda do
+ config.send m, mod do |mod_config|
+ end
+ end.should raise_error /must respond to `configuration`/
+ end
+ end
+
describe "#mock_with" do
+ before { config.stub(:require) }
+
+ it_behaves_like "a configurable framework adapter", :mock_with
+
[:rspec, :mocha, :rr, :flexmock].each do |framework|
context "with #{framework}" do
- it "requires the adapter for #{framework.inspect}" do
+ it "requires the adapter for #{framework}" do
config.should_receive(:require).with("rspec/core/mocking/with_#{framework}")
config.mock_with framework
end
@@ -76,7 +103,6 @@ module RSpec::Core
context "with a module" do
it "sets the mock_framework_adapter to that module" do
- config.stub(:require)
mod = Module.new
config.mock_with mod
config.mock_framework.should eq(mod)
@@ -89,8 +115,6 @@ module RSpec::Core
end
context 'when there are already some example groups defined' do
- before(:each) { config.stub(:require) }
-
it 'raises an error since this setting must be applied before any groups are defined' do
RSpec.world.stub(:example_groups).and_return([double.as_null_object])
expect {
@@ -129,16 +153,14 @@ module RSpec::Core
end
describe "#expect_with" do
- before(:each) do
- # we need to prevent stdlib from being required because it defines a
- # `pass` method that conflicts with our `pass` matcher.
- config.stub(:require)
- end
+ before { config.stub(:require) }
+
+ it_behaves_like "a configurable framework adapter", :expect_with
[
[:rspec, 'rspec/expectations'],
[:stdlib, 'test/unit/assertions']
- ].each do |(framework, required_file)|
+ ].each do |framework, required_file|
context "with #{framework}" do
it "requires #{required_file}" do
config.should_receive(:require).with(required_file)
@@ -147,6 +169,19 @@ module RSpec::Core
end
end
+ it "supports multiple calls" do
+ config.expect_with :rspec
+ config.expect_with :stdlib
+ config.expectation_frameworks.should eq [RSpec::Matchers, Test::Unit::Assertions]
+ end
+
+ it "raises if block given with multiple args" do
+ lambda do
+ config.expect_with :rspec, :stdlib do |mod_config|
+ end
+ end.should raise_error /expect_with only accepts/
+ end
+
it "raises ArgumentError if framework is not supported" do
expect do
config.expect_with :not_supported
@@ -178,11 +213,7 @@ module RSpec::Core
end
describe "#expecting_with_rspec?" do
- before(:each) do
- # prevent minitest assertions from being required and included,
- # as that causes problems in some of our specs.
- config.stub(:require)
- end
+ before { config.stub(:require) }
it "returns false by default" do
config.should_not be_expecting_with_rspec

0 comments on commit 651d8d9

Please sign in to comment.