bogus 0.0.2 → 0.0.3.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/Gemfile.lock +14 -2
- data/Guardfile +5 -0
- data/README.md +7 -249
- data/bogus.gemspec +3 -1
- data/features/.nav +21 -0
- data/features/authors.md +42 -0
- data/{CHANGELOG.md → features/changelog.md} +9 -2
- data/features/configuration_options.feature +1 -2
- data/features/{contract_tests_mocks.feature → contract_tests/contract_tests_mocks.feature} +4 -1
- data/features/{contract_tests_spies.feature → contract_tests/contract_tests_spies.feature} +2 -0
- data/features/{contract_tests_stubs.feature → contract_tests/contract_tests_stubs.feature} +14 -0
- data/features/contract_tests/readme.md +26 -0
- data/features/{return_value_contracts.feature → contract_tests/return_value_contracts.feature} +6 -2
- data/features/{anonymous_doubles.feature → fakes/anonymous_doubles.feature} +24 -8
- data/features/{fake_objects.feature → fakes/fake_objects.feature} +39 -4
- data/features/fakes/global_fake_configuration.feature +88 -0
- data/features/fakes/readme.md +11 -0
- data/features/fakes/replacing_classes.feature +148 -0
- data/features/getting_started.md +36 -0
- data/features/license.md +9 -0
- data/features/readme.md +173 -0
- data/features/safe_stubbing/argument_matchers.feature +30 -0
- data/features/safe_stubbing/readme.md +35 -0
- data/features/{safe_mocking.feature → safe_stubbing/safe_mocking.feature} +14 -0
- data/features/{safe_stubbing.feature → safe_stubbing/safe_stubbing.feature} +18 -4
- data/features/{spies.feature → safe_stubbing/spies.feature} +19 -4
- data/features/step_definitions/rspec_steps.rb +14 -5
- data/features/support/env.rb +4 -0
- data/lib/bogus.rb +2 -0
- data/lib/bogus/adds_recording.rb +11 -7
- data/lib/bogus/any_args.rb +7 -0
- data/lib/bogus/anything.rb +11 -0
- data/lib/bogus/contract_not_fulfilled.rb +17 -10
- data/lib/bogus/copies_classes.rb +2 -6
- data/lib/bogus/creates_fakes_with_stubbed_methods.rb +40 -0
- data/lib/bogus/double.rb +28 -8
- data/lib/bogus/ensures_all_interactions_satisfied.rb +36 -0
- data/lib/bogus/fake.rb +6 -0
- data/lib/bogus/fake_configuration.rb +69 -0
- data/lib/bogus/fakes_classes.rb +21 -0
- data/lib/bogus/has_overwritten_methods.rb +24 -0
- data/lib/bogus/have_received_matcher.rb +63 -0
- data/lib/bogus/injector.rb +36 -14
- data/lib/bogus/interaction.rb +33 -17
- data/lib/bogus/makes_substitute_methods.rb +15 -0
- data/lib/bogus/mocking_dsl.rb +31 -0
- data/lib/bogus/multi_stubber.rb +15 -0
- data/lib/bogus/not_all_expectations_satisfied.rb +27 -0
- data/lib/bogus/overwriten_classes.rb +15 -0
- data/lib/bogus/overwrites_classes.rb +2 -2
- data/lib/bogus/overwrites_methods.rb +42 -0
- data/lib/bogus/proxies_method_calls.rb +23 -0
- data/lib/bogus/proxy_class.rb +25 -16
- data/lib/bogus/public_methods.rb +36 -11
- data/lib/bogus/record_interactions.rb +3 -9
- data/lib/bogus/recording_proxy.rb +5 -0
- data/lib/bogus/registers_created_fakes.rb +2 -1
- data/lib/bogus/resets_overwritten_classes.rb +14 -0
- data/lib/bogus/resets_stubbed_methods.rb +12 -0
- data/lib/bogus/responds_to_everything.rb +11 -0
- data/lib/bogus/rspec.rb +7 -0
- data/lib/bogus/rspec_adapter.rb +17 -0
- data/lib/bogus/rspec_extensions.rb +8 -20
- data/lib/bogus/shadow.rb +60 -0
- data/lib/bogus/verifies_contracts.rb +5 -1
- data/lib/bogus/verifies_stub_definition.rb +5 -0
- data/lib/bogus/version.rb +1 -1
- data/lib/tracks_existence_of_test_doubles.rb +11 -0
- data/spec/bogus/adds_recording_spec.rb +46 -10
- data/spec/bogus/anything_spec.rb +13 -0
- data/spec/bogus/copies_classes_spec.rb +4 -3
- data/spec/bogus/creates_fakes_with_stubbed_methods_spec.rb +121 -0
- data/spec/bogus/double_spec.rb +63 -20
- data/spec/bogus/ensures_all_interactions_satisfied_spec.rb +43 -0
- data/spec/bogus/fake_configuration_spec.rb +99 -0
- data/spec/bogus/fakes_classes_spec.rb +46 -0
- data/spec/bogus/have_received_matcher_spec.rb +56 -0
- data/spec/bogus/interaction_spec.rb +18 -7
- data/spec/bogus/interactions_repository_spec.rb +42 -0
- data/spec/bogus/makes_substitute_methods_spec.rb +24 -0
- data/spec/bogus/mocking_dsl_spec.rb +234 -7
- data/spec/bogus/multi_stubber_spec.rb +31 -0
- data/spec/bogus/overwriten_classes_spec.rb +27 -0
- data/spec/bogus/overwrites_classes_spec.rb +2 -2
- data/spec/bogus/overwrites_methods_spec.rb +107 -0
- data/spec/bogus/proxy_class_spec.rb +6 -0
- data/spec/bogus/record_interactions_spec.rb +3 -4
- data/spec/bogus/registers_created_fakes_spec.rb +8 -0
- data/spec/bogus/resets_overwritten_classes_spec.rb +26 -0
- data/spec/bogus/resets_stubbed_methods_spec.rb +16 -0
- data/spec/bogus/shadow_spec.rb +182 -0
- data/spec/bogus/verifies_contracts_spec.rb +9 -3
- data/spec/bogus/verifies_stub_definition_spec.rb +4 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/fake_creator_of_fakes.rb +15 -0
- data/spec/support/sample_fake.rb +13 -0
- data/spec/tracks_existence_of_test_doubles_spec.rb +26 -0
- metadata +105 -32
- data/lib/bogus/creates_anonymous_stubs.rb +0 -27
- data/lib/bogus/invocation_matcher.rb +0 -27
- data/lib/bogus/rr_proxy.rb +0 -5
- data/spec/bogus/invocation_matcher_spec.rb +0 -26
@@ -1,7 +1,6 @@
|
|
1
1
|
Feature: Configuration Options
|
2
2
|
|
3
|
-
Bogus can be configured, similarly to many other frameworks with a configure block.
|
4
|
-
This feature describes the configuration options available.
|
3
|
+
Bogus can be configured, similarly to many other frameworks with a configure block. This feature describes the configuration options available.
|
5
4
|
|
6
5
|
Scenario: search_modules
|
7
6
|
Given a file named "foo.rb" with:
|
@@ -1,5 +1,7 @@
|
|
1
1
|
Feature: Contract tests with mocks
|
2
2
|
|
3
|
+
Whenever you mock something, you specify a contract on the arguments/return value pair. Bogus can check automatically Whether this contract was satisfied.
|
4
|
+
|
3
5
|
Background:
|
4
6
|
Given a file named "foo.rb" with:
|
5
7
|
"""ruby
|
@@ -45,9 +47,10 @@ Feature: Contract tests with mocks
|
|
45
47
|
it "does not check out the book from library if not available" do
|
46
48
|
student = Student.new
|
47
49
|
mock(library).has_book?("Moby Dick") { false }
|
48
|
-
dont_allow(library).checkout("Moby Dick")
|
49
50
|
|
50
51
|
student.read("Moby Dick", library)
|
52
|
+
|
53
|
+
library.should_not have_received.checkout("Moby Dick")
|
51
54
|
end
|
52
55
|
end
|
53
56
|
"""
|
@@ -1,5 +1,7 @@
|
|
1
1
|
Feature: Contract tests with spies
|
2
2
|
|
3
|
+
Whenever you spy on method invocations, it creates a contract specifying that some method can be called with a particular set of arguments. It can be automatically verified by Bogus, whether the method was actually called with those arguments.
|
4
|
+
|
3
5
|
Background:
|
4
6
|
Given a file named "foo.rb" with:
|
5
7
|
"""ruby
|
@@ -1,5 +1,19 @@
|
|
1
1
|
Feature: Contract tests with stubs
|
2
2
|
|
3
|
+
Whenever you stub any method, a contract is specified on the input/output values of that method.
|
4
|
+
|
5
|
+
When stubbing using the short syntax:
|
6
|
+
|
7
|
+
fake(:fake_name, method_name: :return_value)
|
8
|
+
|
9
|
+
the contract can only be specified on the return value.
|
10
|
+
|
11
|
+
The longer syntax:
|
12
|
+
|
13
|
+
stub(fake).method_name(args) { :return_value }
|
14
|
+
|
15
|
+
will also create a contract on the method input parameters.
|
16
|
+
|
3
17
|
Background:
|
4
18
|
Given a file named "foo.rb" with:
|
5
19
|
"""ruby
|
@@ -0,0 +1,26 @@
|
|
1
|
+
Whenever you write test code like this:
|
2
|
+
|
3
|
+
mock(library).checkout("Moby Dick") { raise NoSuchBookError }
|
4
|
+
|
5
|
+
There are some assumptions this code makes:
|
6
|
+
|
7
|
+
1. There is an object in my system that can play the role of a library.
|
8
|
+
2. The library object has a `#checkout` method that takes one argument.
|
9
|
+
3. The system under test is supposed to call `#checkout` with argument `"Moby Dick"` at least once.
|
10
|
+
4. There is some context in which, given argument "Moby Dick", the `#checkout` method raises `NoSuchBookError`.
|
11
|
+
|
12
|
+
While using fakes makes sure that the assumptions 1 and 2 are satisfied, and assumption number 3 is verified by the mocking system, in order to make sure that the assumption no 4 is also true, you need to write a test for the library object.
|
13
|
+
|
14
|
+
Bogus will not be able to write that test for you, but it can remind you that you should do so.
|
15
|
+
|
16
|
+
Whenever you use named fakes:
|
17
|
+
|
18
|
+
fake(:library)
|
19
|
+
|
20
|
+
Bogus will remember any interactions set up on that fake.
|
21
|
+
|
22
|
+
If you want to verify that you remembered to test all the scenarios specified by stubbing/spying/mocking on the fake object, you can put the following code in the tests for "the real thing" (i.e. the Library class in our example):
|
23
|
+
|
24
|
+
verify_contract(:library)
|
25
|
+
|
26
|
+
This will record all of the interactions you make with that class and make the tests fail if you forget to test some scenario that you recorded using a fake object.
|
data/features/{return_value_contracts.feature → contract_tests/return_value_contracts.feature}
RENAMED
@@ -1,5 +1,9 @@
|
|
1
1
|
Feature: Return value contracts
|
2
2
|
|
3
|
+
In order to be able to record the stubbed value, Bogus requires that the block you pass when stubbing is free of side effects and idempotent.
|
4
|
+
|
5
|
+
If it's not, the behavior of contract verification is not defined.
|
6
|
+
|
3
7
|
Background:
|
4
8
|
Given a file named "foo.rb" with:
|
5
9
|
"""ruby
|
@@ -51,7 +55,7 @@ Feature: Return value contracts
|
|
51
55
|
end
|
52
56
|
"""
|
53
57
|
|
54
|
-
Scenario:
|
58
|
+
Scenario: Bogus makes sure that all the return values recorded by stubbing are also present in tests of the real object
|
55
59
|
Then spec file with following content should fail:
|
56
60
|
"""ruby
|
57
61
|
describe AuthenticationService do
|
@@ -66,7 +70,7 @@ Feature: Return value contracts
|
|
66
70
|
end
|
67
71
|
"""
|
68
72
|
|
69
|
-
Scenario:
|
73
|
+
Scenario: Bogus does not fail the tests if all the recorded values have been also recorded on the real object
|
70
74
|
Then spec file with following content should pass:
|
71
75
|
"""ruby
|
72
76
|
describe AuthenticationService do
|
@@ -1,14 +1,21 @@
|
|
1
1
|
Feature: Anonymous test doubles
|
2
2
|
|
3
|
-
Anonymous test doubles can be useful as a stepping stone towards
|
4
|
-
actual fakes and when migrating from another testing library.
|
3
|
+
Anonymous test doubles can be useful as a stepping stone towards actual fakes and when migrating from another testing library.
|
5
4
|
|
6
|
-
In contrast with other testing libraries, Bogus makes it's fakes
|
7
|
-
respond to all methods by default and makes those calls chainable.
|
8
|
-
This way you can spy on methods without stubbing them first.
|
5
|
+
In contrast with other testing libraries, Bogus makes it's fakes respond to all methods by default and makes those calls chainable. This way you can spy on methods without stubbing them first.
|
9
6
|
|
10
|
-
It is not advisable to use those for anything else than an intermediate step.
|
11
|
-
|
7
|
+
It is not advisable to use those for anything else than an intermediate step. Fakes that mimic an actual class have many more benefits.
|
8
|
+
|
9
|
+
The syntax for defining fakes is:
|
10
|
+
|
11
|
+
fake(method_1: return_value, method_2: proc{return_value2})
|
12
|
+
|
13
|
+
If you pass a proc as a return value to a fake, the proc will be called to obtain the value. This can be used for instance to raise errors in stubbed methods.
|
14
|
+
|
15
|
+
If you want to actually return a proc from a method, you need to use a slightly longer syntax:
|
16
|
+
|
17
|
+
factory = fake()
|
18
|
+
stub(factory).make_validator{ proc{ false } }
|
12
19
|
|
13
20
|
Background:
|
14
21
|
Given a file named "foo.rb" with:
|
@@ -34,7 +41,7 @@ Feature: Anonymous test doubles
|
|
34
41
|
let(:jake) { Student.new("Jake") }
|
35
42
|
|
36
43
|
it "allows stubbing any method with any parameters" do
|
37
|
-
stub(library).register_junior { "the card" }
|
44
|
+
stub(library).register_junior(any_args) { "the card" }
|
38
45
|
|
39
46
|
jake.sign_up(library)
|
40
47
|
|
@@ -58,6 +65,15 @@ Feature: Anonymous test doubles
|
|
58
65
|
end
|
59
66
|
"""
|
60
67
|
|
68
|
+
Scenario: Stubbing methods inline by passing a block
|
69
|
+
Then the following test should pass:
|
70
|
+
"""ruby
|
71
|
+
library = fake(register_junior: proc{ raise "library full!" })
|
72
|
+
expect {
|
73
|
+
library.register_junior("Jake")
|
74
|
+
}.to raise_error("library full!")
|
75
|
+
"""
|
76
|
+
|
61
77
|
Scenario: Mocking any method with any parameters
|
62
78
|
Then spec file with following content should pass:
|
63
79
|
"""ruby
|
@@ -1,7 +1,29 @@
|
|
1
1
|
Feature: Faking existing classes
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
You create a fake by calling the `fake` method:
|
4
|
+
|
5
|
+
fake(fake_name, options) { ClassToCopy }
|
6
|
+
|
7
|
+
If you omit the fake_name, you will get an anonymous fake, otherwise the name will be used to identify the fake for the purposes of contract tests. If you omit the block returning the class to copy, fake name will also be used to guess that.
|
8
|
+
|
9
|
+
Usually you will want the fake to return an instance of the copied class. Otherwise, you can pass `:as => :class` option to copy the class and return the copy, not an instance of it.
|
10
|
+
|
11
|
+
Options can also include methods to stub on the returned fake, which makes:
|
12
|
+
|
13
|
+
fake(:foo, bar: "value")
|
14
|
+
|
15
|
+
Equivalent to:
|
16
|
+
|
17
|
+
foo = fake(:foo)
|
18
|
+
stub(foo).bar(any_args) { "value" }
|
19
|
+
|
20
|
+
For your convenience, Bogus also defines the following macro:
|
21
|
+
|
22
|
+
fake(:foo, bar: "value")
|
23
|
+
|
24
|
+
Which is equivalent to:
|
25
|
+
|
26
|
+
let(:foo) [ fake(:foo, bar: "value")]
|
5
27
|
|
6
28
|
Background:
|
7
29
|
Given a file named "foo.rb" with:
|
@@ -90,5 +112,18 @@ Feature: Faking existing classes
|
|
90
112
|
end
|
91
113
|
"""
|
92
114
|
|
93
|
-
Scenario: Fakes
|
94
|
-
|
115
|
+
Scenario: Fakes with inline return values
|
116
|
+
Then spec file with following content should pass:
|
117
|
+
"""ruby
|
118
|
+
describe "library class fake" do
|
119
|
+
let(:library) { fake(:library, checkout: "checked out",
|
120
|
+
return_book: "returned") }
|
121
|
+
|
122
|
+
it "sets the default return value for provided functions" do
|
123
|
+
library.checkout("Moby Dick").should == "checked out"
|
124
|
+
library.checkout("Three Musketeers").should == "checked out"
|
125
|
+
library.return_book("Moby Dick").should == "returned"
|
126
|
+
library.return_book("Three Musketeers").should == "returned"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
"""
|
@@ -0,0 +1,88 @@
|
|
1
|
+
Feature: Global fake configuration
|
2
|
+
|
3
|
+
In an ideal world, all our fakes would follow the tell-don't-ask principle, which would eliminate the need for stubbing, and would be instances of classes that match the fake name, which would eliminate the need for configuration of things like (`as: :class` / `as: :instance`).
|
4
|
+
|
5
|
+
However, in reality we often need to add this kind of configuration to our fake definitions, and the more collaborators a fake has, the more duplication we introduce this way.
|
6
|
+
|
7
|
+
To eliminate this duplication, Bogus comes with a DSL to configure the fakes in one place, and unify their use in all your tests.
|
8
|
+
|
9
|
+
To globally configure your fakes, all you need to do is to place code like this:
|
10
|
+
|
11
|
+
Bogus.fakes do
|
12
|
+
fake(:fake_name, as: :class, class: proc{SomeClass}) do
|
13
|
+
method_1 { return_value_1 }
|
14
|
+
method_2 return_value_2
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
in your spec helper, or a file required from it.
|
19
|
+
|
20
|
+
Background:
|
21
|
+
Given a file named "foo.rb" with:
|
22
|
+
"""ruby
|
23
|
+
class PublicLibrary
|
24
|
+
def self.books_by_author(name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
"""
|
28
|
+
|
29
|
+
Given a file named "fakes.rb" with:
|
30
|
+
"""ruby
|
31
|
+
Bogus.fakes do
|
32
|
+
fake(:library, as: :class, class: proc{PublicLibrary}) do
|
33
|
+
books_by_author []
|
34
|
+
end
|
35
|
+
end
|
36
|
+
"""
|
37
|
+
|
38
|
+
Scenario: Globally configured fakes have all the properties configured
|
39
|
+
Then spec file with following content should pass:
|
40
|
+
"""ruby
|
41
|
+
require_relative "fakes"
|
42
|
+
|
43
|
+
describe "The library fake" do
|
44
|
+
fake(:library)
|
45
|
+
|
46
|
+
it "is a class" do
|
47
|
+
# because of the as: :class specified in the fake definition
|
48
|
+
library.should be_an_instance_of(Class)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "is a copy of PublicLibrary" do
|
52
|
+
# because of the block passed into configuration
|
53
|
+
library.name.should == "PublicLibrary"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "returns has stubbed books_by_author" do
|
57
|
+
# because of the inline-stubbed books_by_author
|
58
|
+
library.books_by_author("Mark Twain").should == []
|
59
|
+
end
|
60
|
+
end
|
61
|
+
"""
|
62
|
+
|
63
|
+
Scenario: Overwriting stubbed methods using fake macro
|
64
|
+
Then spec file with following content should pass:
|
65
|
+
"""ruby
|
66
|
+
require_relative "fakes"
|
67
|
+
|
68
|
+
describe "The library fake" do
|
69
|
+
fake(:library, books_by_author: ["Some Book"])
|
70
|
+
|
71
|
+
it "can be overridden in the shortcut definition" do
|
72
|
+
library.books_by_author("Charles Dickens").should == ["Some Book"]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
"""
|
76
|
+
|
77
|
+
Scenario: Overwriting stubbed methods using fake helper function
|
78
|
+
Then spec file with following content should pass:
|
79
|
+
"""ruby
|
80
|
+
require_relative "fakes"
|
81
|
+
|
82
|
+
describe "The library fake" do
|
83
|
+
it "can be overridden in the helper" do
|
84
|
+
library = fake(:library, books_by_author: ["Some Book"])
|
85
|
+
library.books_by_author("Charles Dickens").should == ["Some Book"]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
"""
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Fakes in Bogus are essentially lightweight objects that mimic the original object's interface.
|
2
|
+
|
3
|
+
Let's say that we have a `Library` class that is used to manage books and a `Student`, who can interact with the `Library` in some way.
|
4
|
+
|
5
|
+
In order to test the `Student` in isolation, we need to replace the `Library`, with some test double. Typically, you would do that by creating an anonymous stub/mock object and stubbing the required methods on it.
|
6
|
+
|
7
|
+
Using those stubs, you specify the desired interface of the library object.
|
8
|
+
|
9
|
+
The problems with that approach start when you change the `Library` class. For example, you could rename the `#checkout` method to `#checkout_book`. If you used the standard approach, where your stubs are not connected in any way to the real implementation, your tests will keep happily passing, even though the collaborator interface just changed.
|
10
|
+
|
11
|
+
Bogus saves you from this problem, because your fakes have the exact same interface as the real collaborators, so whenever you change the collaborator, but not the tested object, you will get an exception in your tests.
|
@@ -0,0 +1,148 @@
|
|
1
|
+
Feature: Replacing classes with fakes
|
2
|
+
|
3
|
+
Bogus is an opinionated piece of software. One of the opinions we have is that you should use dependency injection to make your code more modular and your classes easier to compose. However, we respect the fact, that this is currently not very popular among Ruby developers.
|
4
|
+
|
5
|
+
In order to make life easier for people who choose not to use Dependency Injection, Bogus makes it convenient to replace chosen classes in your tests with fakes.
|
6
|
+
|
7
|
+
All you need to do, is put the following code in your describe:
|
8
|
+
|
9
|
+
fake_class(FooBar, foo: "bar")
|
10
|
+
|
11
|
+
Which is a shortcut for:
|
12
|
+
|
13
|
+
before do
|
14
|
+
fake_class(FooBar, foo: "bar")
|
15
|
+
end
|
16
|
+
|
17
|
+
Background:
|
18
|
+
Given a file named "app.rb" with:
|
19
|
+
"""ruby
|
20
|
+
require "yaml"
|
21
|
+
|
22
|
+
class Library
|
23
|
+
FILE = "library.yml"
|
24
|
+
|
25
|
+
def self.books
|
26
|
+
YAML.load_file(FILE)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class BookIndex
|
31
|
+
def self.by_author(author)
|
32
|
+
Library.books.select{|book| book[:author] == author}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
"""
|
36
|
+
|
37
|
+
And a file named "spec_helper.rb" with:
|
38
|
+
"""ruby
|
39
|
+
require 'bogus/rspec'
|
40
|
+
|
41
|
+
require_relative 'app'
|
42
|
+
"""
|
43
|
+
|
44
|
+
Scenario: Replacing classes and contracts
|
45
|
+
Given a file named "library_spec.rb" with:
|
46
|
+
"""ruby
|
47
|
+
require_relative 'spec_helper'
|
48
|
+
require 'fileutils'
|
49
|
+
|
50
|
+
describe Library do
|
51
|
+
verify_contract(:library)
|
52
|
+
|
53
|
+
it "reads the books from the yaml file" do
|
54
|
+
books = [{name: "Tom Sawyer", author: "Mark Twain"},
|
55
|
+
{name: "Moby Dick", author: "Herman Melville"}]
|
56
|
+
File.open(Library::FILE, "w") { |f| f.print books.to_yaml }
|
57
|
+
|
58
|
+
Library.books.should == books
|
59
|
+
end
|
60
|
+
|
61
|
+
after do
|
62
|
+
FileUtils.rm_rf(Library::FILE)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
"""
|
66
|
+
|
67
|
+
And a file named "book_index_spec.rb" with:
|
68
|
+
"""ruby
|
69
|
+
require_relative 'spec_helper'
|
70
|
+
|
71
|
+
describe BookIndex do
|
72
|
+
verify_contract(:book_index)
|
73
|
+
|
74
|
+
it "returns books written by author" do
|
75
|
+
tom_sawyer = {name: "Tom Sawyer", author: "Mark Twain"}
|
76
|
+
moby_dick = {name: "Moby Dick", author: "Herman Melville"}
|
77
|
+
|
78
|
+
fake_class(Library, books: [tom_sawyer, moby_dick])
|
79
|
+
|
80
|
+
BookIndex.by_author("Mark Twain").should == [tom_sawyer]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
"""
|
84
|
+
|
85
|
+
When I run `rspec book_index_spec.rb library_spec.rb`
|
86
|
+
Then all the specs should pass
|
87
|
+
|
88
|
+
Scenario: Replacing classes and contracts with a different fake name
|
89
|
+
Given a file named "library_spec.rb" with:
|
90
|
+
"""ruby
|
91
|
+
require_relative 'spec_helper'
|
92
|
+
require 'fileutils'
|
93
|
+
|
94
|
+
describe Library do
|
95
|
+
verify_contract(:book_repository)
|
96
|
+
|
97
|
+
it "reads the books from the yaml file" do
|
98
|
+
books = [{name: "Tom Sawyer", author: "Mark Twain"},
|
99
|
+
{name: "Moby Dick", author: "Herman Melville"}]
|
100
|
+
File.open(Library::FILE, "w") { |f| f.print books.to_yaml }
|
101
|
+
|
102
|
+
Library.books.should == books
|
103
|
+
end
|
104
|
+
|
105
|
+
after do
|
106
|
+
FileUtils.rm_rf(Library::FILE)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
"""
|
110
|
+
|
111
|
+
And a file named "book_index_spec.rb" with:
|
112
|
+
"""ruby
|
113
|
+
require_relative 'spec_helper'
|
114
|
+
|
115
|
+
describe BookIndex do
|
116
|
+
verify_contract(:book_index)
|
117
|
+
|
118
|
+
it "returns books written by author" do
|
119
|
+
tom_sawyer = {name: "Tom Sawyer", author: "Mark Twain"}
|
120
|
+
moby_dick = {name: "Moby Dick", author: "Herman Melville"}
|
121
|
+
|
122
|
+
fake_class(Library, fake_name: :book_repository,
|
123
|
+
books: [tom_sawyer, moby_dick])
|
124
|
+
|
125
|
+
BookIndex.by_author("Mark Twain").should == [tom_sawyer]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
"""
|
129
|
+
|
130
|
+
When I run `rspec book_index_spec.rb library_spec.rb`
|
131
|
+
Then all the specs should pass
|
132
|
+
|
133
|
+
Scenario: Replacing classes with a macro
|
134
|
+
Given a file named "book_index_spec.rb" with:
|
135
|
+
"""ruby
|
136
|
+
require_relative 'spec_helper'
|
137
|
+
|
138
|
+
describe BookIndex do
|
139
|
+
fake_class(Library, books: [])
|
140
|
+
|
141
|
+
it "returns books written by author" do
|
142
|
+
BookIndex.by_author("Mark Twain").should == []
|
143
|
+
end
|
144
|
+
end
|
145
|
+
"""
|
146
|
+
|
147
|
+
When I run `rspec book_index_spec.rb`
|
148
|
+
Then all the specs should pass
|