rspec-fire-roles 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +164 -0
- data/Rakefile +3 -0
- data/features/readme.md +164 -0
- data/features/rspec_fire_roles.feature +112 -0
- data/features/step_definitions/rspec_steps.rb +8 -0
- data/features/support/env.rb +13 -0
- data/lib/rspec/fire/roles.rb +44 -0
- data/lib/rspec/fire/roles/version.rb +7 -0
- data/rspec-fire-roles.gemspec +24 -0
- metadata +94 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Chris Vincent
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
# rspec-fire-roles
|
2
|
+
|
3
|
+
Mocking against roles rather than concrete objects results in more flexible,
|
4
|
+
pluggable object designs. This gem builds upon the capabilities of rspec-fire
|
5
|
+
to make it easier to mock in this style while also knowing with confidence that
|
6
|
+
your objects and their collaborators are speaking through the same interfaces.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem "rspec-fire-roles"
|
13
|
+
|
14
|
+
Or install it yourself with:
|
15
|
+
|
16
|
+
$ gem install rspec-fire-roles
|
17
|
+
|
18
|
+
Add it to your `spec_helper.rb`:
|
19
|
+
|
20
|
+
RSpec.configure do |config|
|
21
|
+
config.include RSpec::Fire
|
22
|
+
config.include RSpec::Fire::Roles
|
23
|
+
end
|
24
|
+
|
25
|
+
## Why
|
26
|
+
|
27
|
+
While rspec-fire allows you to create mocks of specific concrete classes, this
|
28
|
+
isn't quite enough if you'd like to mock a *role* rather than a class. Mocking
|
29
|
+
roles results in more flexible designs, because a given object might play more
|
30
|
+
than one role, and more than one object might play the same role. Thinking in
|
31
|
+
terms of roles rather than objects also assists in the process of *interface
|
32
|
+
discovery*, which is the main purpose of behavior-driven development as an
|
33
|
+
assistant to the design process.
|
34
|
+
|
35
|
+
For more on the topic of mocking roles, see [the seminal paper on the
|
36
|
+
topic](http://jmock.org/oopsla2004.pdf). Also highly recommended reads are the
|
37
|
+
infamous [Growing Object-Oriented Software Guided by
|
38
|
+
Tests](http://amzn.to/VWOwyA) and [Practical Object-Oriented Design in
|
39
|
+
Ruby](http://amzn.to/VWOHtP). See also the example usage below.
|
40
|
+
|
41
|
+
*Full disclosure: Them be affiliate links.*
|
42
|
+
|
43
|
+
## Usage
|
44
|
+
|
45
|
+
Skip directly to the [Relish
|
46
|
+
documentation](https://www.relishapp.com/cvincent/rspec-fire-roles/docs/using-roles-with-rspec-fire)
|
47
|
+
for a simple worked example. Read on for a more detailed explanation.
|
48
|
+
|
49
|
+
Let's say you have a `BatchSender` object. Here's your spec:
|
50
|
+
|
51
|
+
require "roles/notifier"
|
52
|
+
|
53
|
+
describe BatchSender do
|
54
|
+
describe "#send_messages" do
|
55
|
+
it "passes each message to the injected notifier" do
|
56
|
+
notifier = fire_double("Roles::Notifier")
|
57
|
+
instance = BatchSender.new(notifier)
|
58
|
+
notifier.should_receive(:notify).with("Subject 1", "Body 1").once
|
59
|
+
notifier.should_receive(:notify).with("Subject 2").once
|
60
|
+
|
61
|
+
instance.send_messages([["Subject 1", "Body 1"], ["Subject 2"]])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
We're using a regular old `fire_double` here to create the mock, just like with
|
67
|
+
rspec-fire. But notice that, rather than passing in the name of some concrete
|
68
|
+
class which implements `#notify` with two arguments, we pass in the name of a
|
69
|
+
role. Here's that role, defined in `spec/roles/notifier.rb` (though it can be
|
70
|
+
defined anywhere as long as it gets included), which should be required in any
|
71
|
+
isolated unit test which depends upon this role so that rspec-fire recognizes
|
72
|
+
it:
|
73
|
+
|
74
|
+
module Roles
|
75
|
+
class Notifier
|
76
|
+
def notify(subject, body = nil)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
It's just an empty implementation of the interface. Thanks to rspec-fire, our
|
82
|
+
specs will now fail if they depend upon the `Notifier` role but the mock
|
83
|
+
expectations don't match this interface.
|
84
|
+
|
85
|
+
Now here's where rspec-fire-roles comes in. Here's the spec for a concrete
|
86
|
+
object which implements the `Notifier` role:
|
87
|
+
|
88
|
+
require "roles/notifier"
|
89
|
+
|
90
|
+
describe EmailNotifier do
|
91
|
+
implements_role "Roles::Notifier"
|
92
|
+
|
93
|
+
# [...] class-specific specs about notifying via email, not shown
|
94
|
+
end
|
95
|
+
|
96
|
+
The `implements_role` macro ensures that this spec will fail if the
|
97
|
+
`EmailNotifier` class doesn't have the right methods to satisfy the `Notifier`
|
98
|
+
role. Of course, this class can have additional public methods which don't have
|
99
|
+
anything to do with the role; the macro only checks that the methods on
|
100
|
+
`Notifier` are also implemented on `EmailNotifier`. Furthermore, multiple
|
101
|
+
`implements_role` calls can be added to the spec for objects which play more
|
102
|
+
than one role.
|
103
|
+
|
104
|
+
Here's an example of another class in the same system which plays this role,
|
105
|
+
plus another role (for demonstration purposes):
|
106
|
+
|
107
|
+
require "roles/notifier"
|
108
|
+
|
109
|
+
describe SmsNotifier do
|
110
|
+
implements_role "Roles::Notifier"
|
111
|
+
implements_role "Roles::Serializable"
|
112
|
+
|
113
|
+
# [...] class-specific specs about notifying via SMS, not shown
|
114
|
+
end
|
115
|
+
|
116
|
+
You should be able to see what this gains over using rspec-fire alone. Let's
|
117
|
+
say later you decide to extract a Value Object instead of using arrays to
|
118
|
+
represent messages. First you change the role:
|
119
|
+
|
120
|
+
module Roles
|
121
|
+
class Notifier
|
122
|
+
def notify(message)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
Now your `BatchSender` spec will fail because the `fire_double` is expecting
|
128
|
+
the wrong arguments, and your specs for `EmailNotifier` and `SmsNotifier` will
|
129
|
+
fail because the classes still implement the old interface which takes a
|
130
|
+
subject and body. You you can haz fast, isolated unit tests which mock roles
|
131
|
+
rather than objects without having to worry or write extra integration tests to
|
132
|
+
ensure that the objects play well together. Bliss!
|
133
|
+
|
134
|
+
## Future improvements
|
135
|
+
|
136
|
+
* It would be cool to have a nicer DSL for defining roles than just empty
|
137
|
+
implementations.
|
138
|
+
* It would also be cool if roles defined default return values for their
|
139
|
+
`fire_double`s.
|
140
|
+
* The `implements_role` method presently only supports the interface of
|
141
|
+
instances of the class. It would be nice to be able to specify that a role
|
142
|
+
is implemented by class methods instead.
|
143
|
+
* False positives are still possible. If expectations on a `fire_double` are
|
144
|
+
set with the correct number arguments, but the types of the arguments are
|
145
|
+
incorrect (for example, the role expects a single float parameter but a
|
146
|
+
string is passed in), there's no way to detect this disparity because
|
147
|
+
arguments aren't typed. I can imagine placing additional constraints a role
|
148
|
+
such that arguments must match some other role, though I don't know if such
|
149
|
+
a restriction would be worth it. In practice, such a scenario is probably
|
150
|
+
quite rare.
|
151
|
+
* The way rspec-fire works, there is no failure in a dependent class's spec if
|
152
|
+
the role is simply not defined. This is intentional so that using
|
153
|
+
`fire_double`s can be done in both isolation and integration. Be sure your
|
154
|
+
roles are actually being required when you use them, which should be fast
|
155
|
+
enough for isolated tests.
|
156
|
+
* Anything else. Feedback is appreciated!
|
157
|
+
|
158
|
+
## Contributing
|
159
|
+
|
160
|
+
1. Fork it
|
161
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
162
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
163
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
164
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/features/readme.md
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
# rspec-fire-roles
|
2
|
+
|
3
|
+
Mocking against roles rather than concrete objects results in more flexible,
|
4
|
+
pluggable object designs. This gem builds upon the capabilities of rspec-fire
|
5
|
+
to make it easier to mock in this style while also knowing with confidence that
|
6
|
+
your objects and their collaborators are speaking through the same interfaces.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem "rspec-fire-roles"
|
13
|
+
|
14
|
+
Or install it yourself with:
|
15
|
+
|
16
|
+
$ gem install rspec-fire-roles
|
17
|
+
|
18
|
+
Add it to your `spec_helper.rb`:
|
19
|
+
|
20
|
+
RSpec.configure do |config|
|
21
|
+
config.include RSpec::Fire
|
22
|
+
config.include RSpec::Fire::Roles
|
23
|
+
end
|
24
|
+
|
25
|
+
## Why
|
26
|
+
|
27
|
+
While rspec-fire allows you to create mocks of specific concrete classes, this
|
28
|
+
isn't quite enough if you'd like to mock a *role* rather than a class. Mocking
|
29
|
+
roles results in more flexible designs, because a given object might play more
|
30
|
+
than one role, and more than one object might play the same role. Thinking in
|
31
|
+
terms of roles rather than objects also assists in the process of *interface
|
32
|
+
discovery*, which is the main purpose of behavior-driven development as an
|
33
|
+
assistant to the design process.
|
34
|
+
|
35
|
+
For more on the topic of mocking roles, see [the seminal paper on the
|
36
|
+
topic](http://jmock.org/oopsla2004.pdf). Also highly recommended reads are the
|
37
|
+
infamous [Growing Object-Oriented Software Guided by
|
38
|
+
Tests](http://amzn.to/VWOwyA) and [Practical Object-Oriented Design in
|
39
|
+
Ruby](http://amzn.to/VWOHtP). See also the example usage below.
|
40
|
+
|
41
|
+
*Full disclosure: Them be affiliate links.*
|
42
|
+
|
43
|
+
## Usage
|
44
|
+
|
45
|
+
Skip directly to the [Relish
|
46
|
+
documentation](https://www.relishapp.com/cvincent/rspec-fire-roles/docs/using-roles-with-rspec-fire)
|
47
|
+
for a simple worked example. Read on for a more detailed explanation.
|
48
|
+
|
49
|
+
Let's say you have a `BatchSender` object. Here's your spec:
|
50
|
+
|
51
|
+
require "roles/notifier"
|
52
|
+
|
53
|
+
describe BatchSender do
|
54
|
+
describe "#send_messages" do
|
55
|
+
it "passes each message to the injected notifier" do
|
56
|
+
notifier = fire_double("Roles::Notifier")
|
57
|
+
instance = BatchSender.new(notifier)
|
58
|
+
notifier.should_receive(:notify).with("Subject 1", "Body 1").once
|
59
|
+
notifier.should_receive(:notify).with("Subject 2").once
|
60
|
+
|
61
|
+
instance.send_messages([["Subject 1", "Body 1"], ["Subject 2"]])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
We're using a regular old `fire_double` here to create the mock, just like with
|
67
|
+
rspec-fire. But notice that, rather than passing in the name of some concrete
|
68
|
+
class which implements `#notify` with two arguments, we pass in the name of a
|
69
|
+
role. Here's that role, defined in `spec/roles/notifier.rb` (though it can be
|
70
|
+
defined anywhere as long as it gets included), which should be required in any
|
71
|
+
isolated unit test which depends upon this role so that rspec-fire recognizes
|
72
|
+
it:
|
73
|
+
|
74
|
+
module Roles
|
75
|
+
class Notifier
|
76
|
+
def notify(subject, body = nil)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
It's just an empty implementation of the interface. Thanks to rspec-fire, our
|
82
|
+
specs will now fail if they depend upon the `Notifier` role but the mock
|
83
|
+
expectations don't match this interface.
|
84
|
+
|
85
|
+
Now here's where rspec-fire-roles comes in. Here's the spec for a concrete
|
86
|
+
object which implements the `Notifier` role:
|
87
|
+
|
88
|
+
require "roles/notifier"
|
89
|
+
|
90
|
+
describe EmailNotifier do
|
91
|
+
implements_role "Roles::Notifier"
|
92
|
+
|
93
|
+
# [...] class-specific specs about notifying via email, not shown
|
94
|
+
end
|
95
|
+
|
96
|
+
The `implements_role` macro ensures that this spec will fail if the
|
97
|
+
`EmailNotifier` class doesn't have the right methods to satisfy the `Notifier`
|
98
|
+
role. Of course, this class can have additional public methods which don't have
|
99
|
+
anything to do with the role; the macro only checks that the methods on
|
100
|
+
`Notifier` are also implemented on `EmailNotifier`. Furthermore, multiple
|
101
|
+
`implements_role` calls can be added to the spec for objects which play more
|
102
|
+
than one role.
|
103
|
+
|
104
|
+
Here's an example of another class in the same system which plays this role,
|
105
|
+
plus another role (for demonstration purposes):
|
106
|
+
|
107
|
+
require "roles/notifier"
|
108
|
+
|
109
|
+
describe SmsNotifier do
|
110
|
+
implements_role "Roles::Notifier"
|
111
|
+
implements_role "Roles::Serializable"
|
112
|
+
|
113
|
+
# [...] class-specific specs about notifying via SMS, not shown
|
114
|
+
end
|
115
|
+
|
116
|
+
You should be able to see what this gains over using rspec-fire alone. Let's
|
117
|
+
say later you decide to extract a Value Object instead of using arrays to
|
118
|
+
represent messages. First you change the role:
|
119
|
+
|
120
|
+
module Roles
|
121
|
+
class Notifier
|
122
|
+
def notify(message)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
Now your `BatchSender` spec will fail because the `fire_double` is expecting
|
128
|
+
the wrong arguments, and your specs for `EmailNotifier` and `SmsNotifier` will
|
129
|
+
fail because the classes still implement the old interface which takes a
|
130
|
+
subject and body. You you can haz fast, isolated unit tests which mock roles
|
131
|
+
rather than objects without having to worry or write extra integration tests to
|
132
|
+
ensure that the objects play well together. Bliss!
|
133
|
+
|
134
|
+
## Future improvements
|
135
|
+
|
136
|
+
* It would be cool to have a nicer DSL for defining roles than just empty
|
137
|
+
implementations.
|
138
|
+
* It would also be cool if roles defined default return values for their
|
139
|
+
`fire_double`s.
|
140
|
+
* The `implements_role` method presently only supports the interface of
|
141
|
+
instances of the class. It would be nice to be able to specify that a role
|
142
|
+
is implemented by class methods instead.
|
143
|
+
* False positives are still possible. If expectations on a `fire_double` are
|
144
|
+
set with the correct number arguments, but the types of the arguments are
|
145
|
+
incorrect (for example, the role expects a single float parameter but a
|
146
|
+
string is passed in), there's no way to detect this disparity because
|
147
|
+
arguments aren't typed. I can imagine placing additional constraints a role
|
148
|
+
such that arguments must match some other role, though I don't know if such
|
149
|
+
a restriction would be worth it. In practice, such a scenario is probably
|
150
|
+
quite rare.
|
151
|
+
* The way rspec-fire works, there is no failure in a dependent class's spec if
|
152
|
+
the role is simply not defined. This is intentional so that using
|
153
|
+
`fire_double`s can be done in both isolation and integration. Be sure your
|
154
|
+
roles are actually being required when you use them, which should be fast
|
155
|
+
enough for isolated tests.
|
156
|
+
* Anything else. Feedback is appreciated!
|
157
|
+
|
158
|
+
## Contributing
|
159
|
+
|
160
|
+
1. Fork it
|
161
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
162
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
163
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
164
|
+
5. Create new Pull Request
|
@@ -0,0 +1,112 @@
|
|
1
|
+
Feature: Using roles with rspec-fire
|
2
|
+
In order to mock roles rather than objects and build more flexible systems
|
3
|
+
As an avid RSpec user
|
4
|
+
I want to see failing specs when role interfaces are not honored
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given a file named "spec_helper.rb" with:
|
8
|
+
"""ruby
|
9
|
+
require "rubygems"
|
10
|
+
require "bundler/setup"
|
11
|
+
Bundler.setup
|
12
|
+
|
13
|
+
require "rspec/fire"
|
14
|
+
require "rspec/fire/roles"
|
15
|
+
|
16
|
+
RSpec.configure do |config|
|
17
|
+
config.include RSpec::Fire
|
18
|
+
config.include RSpec::Fire::Roles
|
19
|
+
end
|
20
|
+
"""
|
21
|
+
And a file named "batch_sender_spec.rb" with:
|
22
|
+
"""ruby
|
23
|
+
require "spec_helper"
|
24
|
+
require "batch_sender"
|
25
|
+
require "notifier"
|
26
|
+
|
27
|
+
describe BatchSender do
|
28
|
+
describe "#send_messages" do
|
29
|
+
it "passes each message to the injected notifier" do
|
30
|
+
notifier = fire_double("Roles::Notifier")
|
31
|
+
instance = BatchSender.new(notifier)
|
32
|
+
notifier.should_receive(:notify).with("Subject 1", "Body 1").once
|
33
|
+
notifier.should_receive(:notify).with("Subject 2").once
|
34
|
+
|
35
|
+
instance.send_messages([["Subject 1", "Body 1"], ["Subject 2"]])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
"""
|
40
|
+
And a file named "batch_sender.rb" with:
|
41
|
+
"""ruby
|
42
|
+
class BatchSender
|
43
|
+
def initialize(notifier)
|
44
|
+
@notifier = notifier
|
45
|
+
end
|
46
|
+
|
47
|
+
def send_messages(messages)
|
48
|
+
messages.each { |msg| @notifier.notify(*msg.compact) }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
"""
|
52
|
+
And a file named "notifier.rb" with:
|
53
|
+
"""ruby
|
54
|
+
module Roles
|
55
|
+
class Notifier
|
56
|
+
def notify(subject, body = nil); end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
"""
|
60
|
+
And a file named "email_notifier_spec.rb" with:
|
61
|
+
"""ruby
|
62
|
+
require "spec_helper"
|
63
|
+
require "email_notifier"
|
64
|
+
require "notifier"
|
65
|
+
|
66
|
+
describe EmailNotifier do
|
67
|
+
implements_role "Roles::Notifier"
|
68
|
+
end
|
69
|
+
"""
|
70
|
+
|
71
|
+
Scenario: A role with a matching implementation
|
72
|
+
Given a file named "email_notifier.rb" with:
|
73
|
+
"""ruby
|
74
|
+
class EmailNotifier
|
75
|
+
def notify(subject, body = nil); end
|
76
|
+
end
|
77
|
+
"""
|
78
|
+
When I run `rspec batch_sender_spec.rb email_notifier_spec.rb`
|
79
|
+
Then it should pass
|
80
|
+
|
81
|
+
Scenario: A role with a method with the wrong arguments defined
|
82
|
+
Given a file named "email_notifier.rb" with:
|
83
|
+
"""ruby
|
84
|
+
class EmailNotifier
|
85
|
+
def notify(message); end
|
86
|
+
end
|
87
|
+
"""
|
88
|
+
When I run `rspec batch_sender_spec.rb`
|
89
|
+
Then it should pass
|
90
|
+
When I run `rspec email_notifier_spec.rb`
|
91
|
+
Then it should fail with "Incomplete implementation of Roles::Notifier. Parameters for #notify(subject, body = nil) do not match."
|
92
|
+
|
93
|
+
Scenario: A role with an implementation missing a method
|
94
|
+
Given a file named "email_notifier.rb" with:
|
95
|
+
"""ruby
|
96
|
+
class EmailNotifier
|
97
|
+
end
|
98
|
+
"""
|
99
|
+
When I run `rspec batch_sender_spec.rb`
|
100
|
+
Then it should pass
|
101
|
+
When I run `rspec email_notifier_spec.rb`
|
102
|
+
Then it should fail with "Incomplete implementation of Roles::Notifier. #notify(subject, body = nil) is not defined."
|
103
|
+
|
104
|
+
Scenario: A role with an implementation with incorrectly-named arguments
|
105
|
+
Given a file named "email_notifier.rb" with:
|
106
|
+
"""ruby
|
107
|
+
class EmailNotifier
|
108
|
+
def notify(name, content = nil); end
|
109
|
+
end
|
110
|
+
"""
|
111
|
+
When I run `rspec batch_sender_spec.rb email_notifier_spec.rb`
|
112
|
+
Then it should fail with "Incomplete implementation of Roles::Notifier. Parameters for #notify(subject, body = nil) do not match."
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler"
|
3
|
+
Bundler.setup
|
4
|
+
|
5
|
+
require "aruba/cucumber"
|
6
|
+
|
7
|
+
Before do
|
8
|
+
load_paths, requires = ["../../lib"], []
|
9
|
+
load_paths.push($LOAD_PATH.grep %r|bundler/gems|)
|
10
|
+
load_paths << "."
|
11
|
+
|
12
|
+
set_env('RUBYOPT', "-I#{load_paths.join(':')}")
|
13
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "rspec/fire/roles/version"
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module Fire
|
5
|
+
module Roles
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
def incomplete_implementation(role, error)
|
13
|
+
fail "Incomplete implementation of #{role}. #{error}"
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def implements_role(role)
|
18
|
+
role = role.split("::").inject(Kernel) { |last, const| last.const_get(const) }
|
19
|
+
|
20
|
+
describe "#{role} interface" do
|
21
|
+
role.public_instance_methods(false).each do |m|
|
22
|
+
m = role.public_instance_method(m)
|
23
|
+
params = m.parameters.map { |(opt, name)| name.to_s + (opt == :opt ? " = nil" : "") }.join(", ")
|
24
|
+
|
25
|
+
it "defines ##{m.name}(#{params})" do
|
26
|
+
begin
|
27
|
+
klass = subject.class
|
28
|
+
imp = klass.public_instance_method(m.name.to_sym)
|
29
|
+
|
30
|
+
if imp.parameters != m.parameters
|
31
|
+
incomplete_implementation(role, "Parameters for ##{m.name}(#{params}) do not match.")
|
32
|
+
end
|
33
|
+
rescue NameError
|
34
|
+
puts $!.inspect
|
35
|
+
incomplete_implementation(role, "##{m.name}(#{params}) is not defined.")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "rspec/fire/roles/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "rspec-fire-roles"
|
8
|
+
gem.version = RSpec::Fire::Roles::VERSION
|
9
|
+
gem.authors = ["Chris Vincent"]
|
10
|
+
gem.email = ["c.j.vincent@gmail.com"]
|
11
|
+
gem.description = %q{Mock roles, not objects. For use with rspec-fire.}
|
12
|
+
gem.summary = gem.description
|
13
|
+
gem.homepage = "http://github.com/cvincent/rspec-fire-roles"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_development_dependency "cucumber", "~> 1.2"
|
21
|
+
gem.add_development_dependency "aruba", "~> 0.5"
|
22
|
+
|
23
|
+
gem.add_dependency "rspec-fire", "~> 1.1"
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rspec-fire-roles
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Chris Vincent
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-25 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: cucumber
|
16
|
+
requirement: &2177347760 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.2'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2177347760
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: aruba
|
27
|
+
requirement: &2177347260 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0.5'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2177347260
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec-fire
|
38
|
+
requirement: &2177346800 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '1.1'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2177346800
|
47
|
+
description: Mock roles, not objects. For use with rspec-fire.
|
48
|
+
email:
|
49
|
+
- c.j.vincent@gmail.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- .gitignore
|
55
|
+
- Gemfile
|
56
|
+
- LICENSE.txt
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- features/readme.md
|
60
|
+
- features/rspec_fire_roles.feature
|
61
|
+
- features/step_definitions/rspec_steps.rb
|
62
|
+
- features/support/env.rb
|
63
|
+
- lib/rspec/fire/roles.rb
|
64
|
+
- lib/rspec/fire/roles/version.rb
|
65
|
+
- rspec-fire-roles.gemspec
|
66
|
+
homepage: http://github.com/cvincent/rspec-fire-roles
|
67
|
+
licenses: []
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.8.16
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Mock roles, not objects. For use with rspec-fire.
|
90
|
+
test_files:
|
91
|
+
- features/readme.md
|
92
|
+
- features/rspec_fire_roles.feature
|
93
|
+
- features/step_definitions/rspec_steps.rb
|
94
|
+
- features/support/env.rb
|