Skip to content

Commit

Permalink
Add cukes for new stub_const feature.
Browse files Browse the repository at this point in the history
Closes rspec#144.
  • Loading branch information
myronmarston committed Jun 2, 2012
1 parent ecfb7f6 commit e31f4a4
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 0 deletions.
3 changes: 3 additions & 0 deletions features/.nav
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
- explicit.feature
- general_matchers.feature
- type_matchers.feature
- stubbing_constants:
- stub_defined_constant.feature
- stub_undefined_constant.feature
- outside_rspec:
- configuration.feature
- standalone.feature
62 changes: 62 additions & 0 deletions features/stubbing_constants/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
## Stubbing Constants

Support is provided for stubbing constants. Like with method stubs, the
stubbed constants will be restored to their original state when a method
completes.

``` ruby
stub_const("Foo", fake_foo)
Foo # => fake_foo
```

Stubbed constant names must be fully qualified; the current module
nesting is not considered.

``` ruby
module MyGem
class SomeClass; end
end

module MyGem
describe "Something" do
let(:fake_class) { Class.new }

it "accidentally stubs the wrong constant" do
# this stubs ::SomeClass (in the top-level namespace),
# not MyGem::SomeClass like you probably mean.
stub_const("SomeClass", fake_class)
end

it "stubs the right constant" do
stub_const("MyGem::SomeClass", fake_class)
end
end
end
```

When you stub a constant that is a module or a class, nested
constants on the original module or class are not transferred
by default, but you can use the `:transfer_nested_constants`
option to tell rspec-mocks to transfer them:

``` ruby
class CardDeck
SUITS = [:Spades, :Diamonds, :Clubs, :Hearts]
NUM_CARDS = 52
end

fake_class = Class.new
stub_const("CardDeck", fake_class)
CardDeck # => fake_class
CardDeck::SUITS # => raises uninitialized constant error
CardDeck::NUM_CARDS # => raises uninitialized constant error

stub_const("CardDeck", fake_class, :transfer_nested_constants => true)
CardDeck::SUITS # => [:Spades, :Diamonds, :Clubs, :Hearts]
CardDeck::NUM_CARDS # => 52

stub_const("CardDeck", fake_class, :transfer_nested_constants => [:SUITS])
CardDeck::SUITS # => [:Spades, :Diamonds, :Clubs, :Hearts]
CardDeck::NUM_CARDS # => raises uninitialized constant error
```

79 changes: 79 additions & 0 deletions features/stubbing_constants/stub_defined_constant.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
Feature: Stub Defined Constant

Use `stub_const` to stub constants. When the constant is already defined,
the stubbed value will replace the original value for the duration of the
example.

Scenario: Stub top-level constant
Given a file named "stub_const_spec.rb" with:
"""ruby
FOO = 7
describe "stubbing FOO" do
it "can stub FOO with a different value" do
stub_const("FOO", 5)
FOO.should eq(5)
end
it "restores the stubbed constant when the example completes" do
FOO.should eq(7)
end
end
"""
When I run `rspec stub_const_spec.rb`
Then the examples should all pass

Scenario: Stub nested constant
Given a file named "stub_const_spec.rb" with:
"""ruby
module MyGem
class SomeClass
FOO = 7
end
end
module MyGem
describe SomeClass do
it "stubs the nested constant when it is fully qualified" do
stub_const("MyGem::SomeClass::FOO", 5)
SomeClass::FOO.should eq(5)
end
end
end
"""
When I run `rspec stub_const_spec.rb`
Then the examples should all pass

Scenario: Transfer nested constants
Given a file named "stub_const_spec.rb" with:
"""ruby
module MyGem
class SomeClass
FOO = 7
end
end
module MyGem
describe SomeClass do
let(:fake_class) { Class.new }
it "does not transfer nested constants by default" do
stub_const("MyGem::SomeClass", fake_class)
expect { SomeClass::FOO }.to raise_error(NameError)
end
it "transfers nested constants when using :transfer_nested_constants => true" do
stub_const("MyGem::SomeClass", fake_class, :transfer_nested_constants => true)
SomeClass::FOO.should eq(7)
end
it "can specify a list of nested constants to transfer" do
stub_const("MyGem::SomeClass", fake_class, :transfer_nested_constants => [:FOO])
SomeClass::FOO.should eq(7)
end
end
end
"""
When I run `rspec stub_const_spec.rb`
Then the examples should all pass

50 changes: 50 additions & 0 deletions features/stubbing_constants/stub_undefined_constant.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
Feature: Stub Undefined Constant

Use `stub_const` to stub constants. When the constant is not already defined,
all the necessary intermediary modules will be dynamically created. When the
example completes, the intermediary module constants will be removed to return
the constant state to how it started.

Scenario: Stub top-level constant
Given a file named "stub_const_spec.rb" with:
"""ruby
describe "stubbing FOO" do
it "can stub undefined constant FOO" do
stub_const("FOO", 5)
FOO.should eq(5)
end
it "undefines the constant when the example completes" do
expect { FOO }.to raise_error(NameError)
end
end
"""
When I run `rspec stub_const_spec.rb`
Then the examples should all pass

Scenario: Stub nested constant
Given a file named "stub_const_spec.rb" with:
"""ruby
module MyGem
class SomeClass
end
end
module MyGem
describe SomeClass do
it "can stub an arbitrarily deep constant that is undefined" do
defined?(SomeClass::A).should be_false
stub_const("MyGem::SomeClass::A::B::C", 3)
SomeClass::A::B::C.should eq(3)
SomeClass::A.should be_a(Module)
end
it 'undefines the intermediary constants that were dynamically created' do
defined?(SomeClass).should be_true
defined?(SomeClass::A).should be_false
end
end
end
"""
When I run `rspec stub_const_spec.rb`
Then the examples should all pass

0 comments on commit e31f4a4

Please sign in to comment.