aidmock 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/README.textile +239 -2
- data/VERSION +1 -1
- data/aidmock.gemspec +4 -2
- data/examples/integration_spec.rb +56 -0
- data/lib/aidmock.rb +14 -4
- data/lib/aidmock/frameworks.rb +10 -0
- data/lib/aidmock/frameworks/rspec.rb +4 -0
- data/spec/aidmock/frameworks/rspec_spec.rb +14 -0
- data/spec/aidmock_spec.rb +14 -0
- metadata +5 -3
data/README.textile
CHANGED
@@ -1,5 +1,242 @@
|
|
1
1
|
h1. Aidmock
|
2
2
|
|
3
|
-
|
3
|
+
Aidmock is an safe mock and interfacing library for Ruby.
|
4
4
|
|
5
|
-
|
5
|
+
Aidmock doesn't target any specific mock or test framework. Our goal is to provide a wide solution, that supports the major mock and test frameworks. For now we are only supporting RSpec 2 and RSpec Mocks, but more drivers for other frameworks will be available soon.
|
6
|
+
|
7
|
+
The basic idea of Aidmock is we believe that Mocks are good, but they can be scary too, leading to false positives. Aidmock tries to make it safer. To read more about motivation, see the "motivation":https://github.com/wilkerlucio/aidmock/wiki/Motivation page.
|
8
|
+
|
9
|
+
Aidmock also generate some sanity checks for your interfaces, so defining interfaces can be a nice point when starting the defition of your classes. Let's start with setup and configuration, then we will see how to define our interfaces.
|
10
|
+
|
11
|
+
h2. Installation
|
12
|
+
|
13
|
+
To install Aidmock just run:
|
14
|
+
|
15
|
+
bc. gem install aidmock
|
16
|
+
|
17
|
+
Or add it to your Gemfile:
|
18
|
+
|
19
|
+
bc. gem "aidmock"
|
20
|
+
|
21
|
+
h2. Configuration
|
22
|
+
|
23
|
+
In order to use Aidmock, you need to configure it on your test environment. Since it's only working on RSpec for now you need to configure your @spec_helper@:
|
24
|
+
|
25
|
+
bc..
|
26
|
+
RSpec.configure do |config|
|
27
|
+
config.before :all do
|
28
|
+
Aidmock.setup # it will do any nescessary setup, like extending your framework mocks
|
29
|
+
end
|
30
|
+
|
31
|
+
config.after :each do
|
32
|
+
Aidmock.verify # it will verify created mocks after each spec
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# load or write your interfaces here
|
37
|
+
|
38
|
+
Aidmock::Sanity.sanitize # it will run sanity checks, just call it after have all interfaces defined
|
39
|
+
|
40
|
+
h2. How It Works
|
41
|
+
|
42
|
+
Aidmock runs after each test you do. It will get the mocks/stubs defined by your test and match these doubles with the interface you have defined for that object. If your mock doesn't respect your interface, it will raise an error, preventing you from having a false positive. Simple :)
|
43
|
+
|
44
|
+
h3. Remember to constrain your mocks
|
45
|
+
|
46
|
+
Sometimes you wanna do something like this:
|
47
|
+
|
48
|
+
bc. obj = mock
|
49
|
+
obj.should_receive(:something)
|
50
|
+
|
51
|
+
With Aidmock we can't detect the class with only this, so, for a safe mocking on this cases you should constrain the mock for one class or module that you already interfaced:
|
52
|
+
|
53
|
+
bc. obj = mock.constrained_to(SomeInterface)
|
54
|
+
obj.should_receive(:something)
|
55
|
+
|
56
|
+
This way can clear parse it.
|
57
|
+
|
58
|
+
h3. No interface warn
|
59
|
+
|
60
|
+
By default, Aidmock will warn you when you try to mock/stub something that you haven't interfaced. You can remove this warn with following snippet:
|
61
|
+
|
62
|
+
bc. Aidmock.warn_undefined_interface = false
|
63
|
+
|
64
|
+
h2. Interfacing
|
65
|
+
|
66
|
+
The major point of Aidmock is interfacing: it's when you define how your object's are supposed to work. It's really like you define methods on static languages as C or Java, but it's more dynamic and cool to work with. :)
|
67
|
+
|
68
|
+
Let's try it and define a simple interface:
|
69
|
+
|
70
|
+
bc.. Aidmock.interface Person do
|
71
|
+
method :first_name, String
|
72
|
+
method :first_name=, nil, String
|
73
|
+
|
74
|
+
method :last_name, String
|
75
|
+
method :last_name=, nil, String
|
76
|
+
|
77
|
+
method :full_name, String
|
78
|
+
end
|
79
|
+
|
80
|
+
p. Ok, let's take some time to look at what we are doing. We use a DSL created by Aidmock to define the class interface. In this case we use @method@ to describe an instance method. The first argument is the method name, the second one is the return, and after return we can send any number of params we want: they are the arguments.
|
81
|
+
|
82
|
+
The return and arguments in fact are matchers; they match if the value used corresponds to the matcher. In the example above, we used two different kind of matchers: the @KindOfMatcher@ (when we used the String class, it created this matcher) and the @AnythingMatcher@ (when we used nil).
|
83
|
+
|
84
|
+
This interface will automatically create some specs that check if these methods are defined on @Person@ class and if they respect the method arity. And most important, when you stub or mock the Person, it will check the interface and if you are mocking with correct params and return values; this will make your double safe.
|
85
|
+
|
86
|
+
h3. Class Methods
|
87
|
+
|
88
|
+
For interfacing class methods just use @class_method@ instead of @method@. Everything else is same for both.
|
89
|
+
|
90
|
+
h3. Method Names
|
91
|
+
|
92
|
+
The method names can be specified as symbol or regexp. Use symbol for method exact names and regexp for dynamic names.
|
93
|
+
|
94
|
+
bc. Aidmock.Interface MyAR do
|
95
|
+
method :table, Symbol
|
96
|
+
method /find_by_.+/, String
|
97
|
+
end
|
98
|
+
|
99
|
+
h2. Matchers
|
100
|
+
|
101
|
+
In section above we saw a simple example of how to create an interface. Now we will go deeper and see all the available matchers and how to use them.
|
102
|
+
|
103
|
+
h3(#conversions). Matcher Conversion
|
104
|
+
|
105
|
+
In most of cases, instead of creating a matcher directly, you will use a "Matcher Conversion". It takes simple values and create matchers based on them. In the table above you can see all available conversions:
|
106
|
+
|
107
|
+
|_. Object Type |_. Matcher |_. Description |
|
108
|
+
| Class | KindOfMatcher | if object is class, it will create a @KindOfMatcher@ with the given class |
|
109
|
+
| Array | AnyMatcher | if the object is an array, it will create an @AnyMatcher@, where each item of array will be an matcher |
|
110
|
+
| nil | AnythingMatcher | if the object is nil, it will create an @AnythingMatcher@ |
|
111
|
+
| Symbol | DuckTypeMatcher | if the object is a symbol, it will create an @DuckTypeMatcher@ that responds to it |
|
112
|
+
| Hash | HashMatcher | if the object is an hash, it will create an @HashMatcher@ with given hash |
|
113
|
+
|
114
|
+
h3(#any_matcher). AnyMatcher
|
115
|
+
|
116
|
+
This matcher can be used when you want to have more than one matcher option. It takes a list of matchers (or use a value to be converted by "conversions":#conversions) and it will match if any of these matchers matches.
|
117
|
+
|
118
|
+
DSL helper: @any_of(*matchers)@
|
119
|
+
Conversion: use an array
|
120
|
+
|
121
|
+
Example:
|
122
|
+
|
123
|
+
bc. method :concat, String, [String, Fixnum]
|
124
|
+
method :concat, String, any_of(String, Fixnum) # same as line above
|
125
|
+
|
126
|
+
h3(#anything_matcher). AnythingMatcher
|
127
|
+
|
128
|
+
This matcher will simply accept anything.
|
129
|
+
|
130
|
+
DSL Helper: @anything@
|
131
|
+
Conversion: use a @nil@
|
132
|
+
|
133
|
+
Example:
|
134
|
+
|
135
|
+
bc. method :puts, nil
|
136
|
+
method :puts, anything #same as line above
|
137
|
+
|
138
|
+
h3(#duck_type_matcher). DuckTypeMatcher
|
139
|
+
|
140
|
+
Duck type matcher will check if an object responds to all the given methods.
|
141
|
+
|
142
|
+
DSL Helper: @respond_to(*methods)@
|
143
|
+
Conversion: use a symbol
|
144
|
+
|
145
|
+
Example:
|
146
|
+
|
147
|
+
bc. method :write, :to_s
|
148
|
+
method :write, respond_to(:to_s) # same as line above
|
149
|
+
|
150
|
+
h3(#instance_of_matcher). InstanceOfMatcher
|
151
|
+
|
152
|
+
Check if the object is the instance of given class.
|
153
|
+
|
154
|
+
DSL Helper: @instance_of(klass)@
|
155
|
+
|
156
|
+
Example:
|
157
|
+
|
158
|
+
bc. method :to_s, instance_of(String)
|
159
|
+
|
160
|
+
h3(#kind_of_matcher). KindOfMatcher
|
161
|
+
|
162
|
+
Check the object is the kind of given class.
|
163
|
+
|
164
|
+
DSL Helper: @kind_of(klass)@
|
165
|
+
Conversion: use any class
|
166
|
+
|
167
|
+
Example:
|
168
|
+
|
169
|
+
bc. method :find, ActiveRecord::Base
|
170
|
+
method :find, kind_of(ActiveRecord::Base) # same as above line
|
171
|
+
|
172
|
+
h3(#hash_matcher). HashMatcher
|
173
|
+
|
174
|
+
The hash matcher checks if the argument is a hash and each key defined on it. If the user sends a hash with a key that is not present on the hash definition, it will fail. It also checks the value type of each key. By default it will ignore if the user doesn't send all expected options (which is a common pattern for options arguments), but it has a strict mode that will require all the keys to be defined.
|
175
|
+
|
176
|
+
DSL Helper: @hash_including(hash, strict = false)@
|
177
|
+
Conversion: use any hash
|
178
|
+
|
179
|
+
Example:
|
180
|
+
|
181
|
+
bc. method :find, {:conditions => [String, Array], :order => String}
|
182
|
+
method :find, has_including(:conditions => [String, Array], :order => String) # same as above
|
183
|
+
method :find, has_including({:conditions => [String, Array], :order => String}, true) # will require to be called with all keys defined
|
184
|
+
|
185
|
+
h3(#not_nil_arg_matcher). NotNilArgMatcher
|
186
|
+
|
187
|
+
By default, all matchers will accept a @nil@ value. If you want to require the value (making a @nil@ return false), you can use this matcher. You also need to send a matcher as param (to check when value is not nil, you can use the conversions too).
|
188
|
+
|
189
|
+
DSL Helper: @not_nil(matcher)@ or @nn(matcher)@
|
190
|
+
|
191
|
+
Example:
|
192
|
+
|
193
|
+
bc. method :name=, nil, nn(String)
|
194
|
+
method :name=, nil, not_nil(String) # same as above
|
195
|
+
|
196
|
+
h3(#optional_arg_matcher). OptionalArgMatcher
|
197
|
+
|
198
|
+
When you want optional arguments, the @OptionalArgMatcher@ will solve it for you. This matcher is only valid for arguments.
|
199
|
+
|
200
|
+
DSL Helper: @optional(matcher)@ or @o(matcher)@
|
201
|
+
|
202
|
+
Example:
|
203
|
+
|
204
|
+
bc. method :something, nil, o(String)
|
205
|
+
method :something, nil, optional(String) # same as above
|
206
|
+
|
207
|
+
h3(#splat_arg_matcher). SplatArgMatcher
|
208
|
+
|
209
|
+
When you want to use splat arguments (example: @def thing(*args)@) this matcher will interface it, you also need to send an matcher (or conversion) to it, and each value of splat will be matched by this matcher. This matcher is only valid for arguments.
|
210
|
+
|
211
|
+
DSL Helper: @splat(matcher)@ or @s(matcher)@
|
212
|
+
|
213
|
+
Example:
|
214
|
+
|
215
|
+
bc. method :something, s(nil) # will accept splat with anything(nil will be converted one AnythingMatcher)
|
216
|
+
method :something, splat(nil) # same as above
|
217
|
+
|
218
|
+
h2. Class Inheritance and Modules
|
219
|
+
|
220
|
+
Aidmock respects your class inheritance and module definition, so, the below example will be valid:
|
221
|
+
|
222
|
+
bc.. class Animal
|
223
|
+
def scream(noise)
|
224
|
+
puts noise.to_s + "!!!"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
class Dog < Animal
|
229
|
+
end
|
230
|
+
|
231
|
+
Aidmock.interface Animal do
|
232
|
+
method :scream, String, :to_s
|
233
|
+
end
|
234
|
+
|
235
|
+
it "test inheritance" do
|
236
|
+
dog = Dog.allocate
|
237
|
+
dog.stub(:scream).with("ha").and_return("ha!!!") # this stub will be verified as you expect
|
238
|
+
end
|
239
|
+
|
240
|
+
h2. Feedback
|
241
|
+
|
242
|
+
Aidmock still be somekind of experimental project, any feedback will help a lot. Please use github issues for reporting any bug and/or suggestion :)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/aidmock.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{aidmock}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Wilker Lucio"]
|
12
|
-
s.date = %q{2011-01-
|
12
|
+
s.date = %q{2011-01-17}
|
13
13
|
s.description = %q{Aidmock, safe mocking and interfacing for Ruby}
|
14
14
|
s.email = %q{wilkerlucio@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -23,6 +23,7 @@ Gem::Specification.new do |s|
|
|
23
23
|
"Rakefile",
|
24
24
|
"VERSION",
|
25
25
|
"aidmock.gemspec",
|
26
|
+
"examples/integration_spec.rb",
|
26
27
|
"examples/mock_spec.rb",
|
27
28
|
"lib/aidmock.rb",
|
28
29
|
"lib/aidmock/basic_object.rb",
|
@@ -55,6 +56,7 @@ Gem::Specification.new do |s|
|
|
55
56
|
"spec/aidmock/sanity_spec.rb",
|
56
57
|
"spec/aidmock_spec.rb",
|
57
58
|
"spec/spec_helper.rb",
|
59
|
+
"examples/integration_spec.rb",
|
58
60
|
"examples/mock_spec.rb"
|
59
61
|
]
|
60
62
|
|
@@ -0,0 +1,56 @@
|
|
1
|
+
$: << File.expand_path("../../lib", __FILE__)
|
2
|
+
require 'aidmock'
|
3
|
+
|
4
|
+
RSpec.configure do |config|
|
5
|
+
config.before :all do
|
6
|
+
Aidmock.setup
|
7
|
+
end
|
8
|
+
|
9
|
+
config.after :each do
|
10
|
+
Aidmock.verify
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Address
|
15
|
+
def full_address
|
16
|
+
"testing"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Person
|
21
|
+
def name
|
22
|
+
"John"
|
23
|
+
end
|
24
|
+
|
25
|
+
def full_info(address)
|
26
|
+
"#{name} - #{address.full_address}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Aidmock.interface Person do
|
31
|
+
method :name, String
|
32
|
+
method :full_info, String, :full_address
|
33
|
+
end
|
34
|
+
|
35
|
+
Aidmock.interface Address do
|
36
|
+
method :full_address, String
|
37
|
+
end
|
38
|
+
|
39
|
+
Aidmock::Sanity.sanitize
|
40
|
+
|
41
|
+
describe Person do
|
42
|
+
before :each do
|
43
|
+
@person = Person.new
|
44
|
+
end
|
45
|
+
|
46
|
+
context ".full_info" do
|
47
|
+
it "should return some" do
|
48
|
+
address = mock.constrained_to(Address)
|
49
|
+
address.stub(:full_address) { "my street" }
|
50
|
+
|
51
|
+
@person.stub(:name).and_return("my name")
|
52
|
+
|
53
|
+
@person.full_info(address).should == "my name - my street"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/aidmock.rb
CHANGED
@@ -26,6 +26,7 @@ module Aidmock
|
|
26
26
|
autoload :MethodDescriptor, 'aidmock/method_descriptor'
|
27
27
|
autoload :VoidClass, 'aidmock/void_class'
|
28
28
|
autoload :Frameworks, 'aidmock/frameworks'
|
29
|
+
autoload :TestFrameworks, 'aidmock/test_frameworks'
|
29
30
|
autoload :Matchers, 'aidmock/matchers'
|
30
31
|
autoload :Sanity, 'aidmock/sanity'
|
31
32
|
|
@@ -50,6 +51,18 @@ module Aidmock
|
|
50
51
|
@interfaces ||= {}
|
51
52
|
end
|
52
53
|
|
54
|
+
def framework
|
55
|
+
::Aidmock::Frameworks::RSpec
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_framework
|
59
|
+
::Aidmock::TestFrameworks::RSpec
|
60
|
+
end
|
61
|
+
|
62
|
+
def setup
|
63
|
+
framework.extend_doubles
|
64
|
+
end
|
65
|
+
|
53
66
|
protected
|
54
67
|
|
55
68
|
def verify_double(double)
|
@@ -87,13 +100,10 @@ module Aidmock
|
|
87
100
|
end
|
88
101
|
|
89
102
|
def extract_class(object)
|
103
|
+
return object.aidmock_class if object.respond_to? :aidmock_class and object.aidmock_class != nil
|
90
104
|
object.instance_of?(Class) ? object : object.class
|
91
105
|
end
|
92
106
|
|
93
|
-
def framework
|
94
|
-
::Aidmock::Frameworks::RSpec
|
95
|
-
end
|
96
|
-
|
97
107
|
def create_or_update_interface(klass, &block)
|
98
108
|
interface = interfaces[klass] || Interface.new(klass)
|
99
109
|
interface.instance_eval &block
|
data/lib/aidmock/frameworks.rb
CHANGED
@@ -23,5 +23,15 @@ module Aidmock
|
|
23
23
|
autoload :RSpec, 'aidmock/frameworks/rspec'
|
24
24
|
|
25
25
|
MockDescriptor = Struct.new(:object, :method, :result, :arguments)
|
26
|
+
|
27
|
+
module DoubleExtensions
|
28
|
+
attr_reader :aidmock_class
|
29
|
+
|
30
|
+
def constrained_to(interface)
|
31
|
+
@aidmock_class = interface
|
32
|
+
|
33
|
+
self
|
34
|
+
end
|
35
|
+
end
|
26
36
|
end
|
27
37
|
end
|
@@ -30,6 +30,20 @@ end
|
|
30
30
|
describe Aidmock::Frameworks::RSpec do
|
31
31
|
framework = Aidmock::Frameworks::RSpec
|
32
32
|
|
33
|
+
context ".extend_doubles" do
|
34
|
+
it "save and retrieve aidmock class" do
|
35
|
+
framework.extend_doubles
|
36
|
+
obj = mock.constrained_to(String)
|
37
|
+
obj.aidmock_class.should == String
|
38
|
+
end
|
39
|
+
|
40
|
+
it "save and retrieve aidmock class for stub" do
|
41
|
+
framework.extend_doubles
|
42
|
+
obj = stub.constrained_to(String)
|
43
|
+
obj.aidmock_class.should == String
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
33
47
|
context ".mocks" do
|
34
48
|
context "caller object" do
|
35
49
|
it "return the object" do
|
data/spec/aidmock_spec.rb
CHANGED
@@ -113,6 +113,20 @@ describe Aidmock do
|
|
113
113
|
end
|
114
114
|
|
115
115
|
context ".extract_class" do
|
116
|
+
it "return aidmock_class if object responds to it" do
|
117
|
+
obj = mock
|
118
|
+
obj.stub!(:aidmock_class) { Fixnum }
|
119
|
+
|
120
|
+
Aidmock.send(:extract_class, obj).should == Fixnum
|
121
|
+
end
|
122
|
+
|
123
|
+
it "return class itself if object aidmock_class is nil" do
|
124
|
+
obj = mock
|
125
|
+
obj.stub!(:aidmock_class) { nil }
|
126
|
+
|
127
|
+
Aidmock.send(:extract_class, obj).should == ::RSpec::Mocks::Mock
|
128
|
+
end
|
129
|
+
|
116
130
|
it "return the object class if it's an instance" do
|
117
131
|
Aidmock.send(:extract_class, "string").should == String
|
118
132
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
7
|
+
- 2
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Wilker Lucio
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-01-
|
17
|
+
date: 2011-01-17 00:00:00 -03:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -34,6 +34,7 @@ files:
|
|
34
34
|
- Rakefile
|
35
35
|
- VERSION
|
36
36
|
- aidmock.gemspec
|
37
|
+
- examples/integration_spec.rb
|
37
38
|
- examples/mock_spec.rb
|
38
39
|
- lib/aidmock.rb
|
39
40
|
- lib/aidmock/basic_object.rb
|
@@ -92,4 +93,5 @@ test_files:
|
|
92
93
|
- spec/aidmock/sanity_spec.rb
|
93
94
|
- spec/aidmock_spec.rb
|
94
95
|
- spec/spec_helper.rb
|
96
|
+
- examples/integration_spec.rb
|
95
97
|
- examples/mock_spec.rb
|