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 CHANGED
@@ -1,2 +1,5 @@
1
1
  /.bundle/
2
2
  .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/README.textile CHANGED
@@ -1,5 +1,242 @@
1
1
  h1. Aidmock
2
2
 
3
- Hi, we are under development, but if you wanna know more about what this project is for, check our "motivation":https://github.com/wilkerlucio/aidmock/wiki/Motivation page.
3
+ Aidmock is an safe mock and interfacing library for Ruby.
4
4
 
5
- We will have something usable for you in soon :)
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
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.1.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-10}
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
@@ -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
@@ -43,6 +43,10 @@ module Aidmock
43
43
  mocks
44
44
  end
45
45
 
46
+ def extend_doubles
47
+ ::RSpec::Mocks::Mock.send(:include, ::Aidmock::Frameworks::DoubleExtensions)
48
+ end
49
+
46
50
  protected
47
51
 
48
52
  def parse_double_result(double)
@@ -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
- - 1
7
+ - 2
8
8
  - 0
9
- version: 0.1.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-10 00:00:00 -03:00
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