adequack 0.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.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +229 -0
- data/Rakefile +5 -0
- data/adequack.gemspec +25 -0
- data/lib/adequack/core.rb +43 -0
- data/lib/adequack/integration/rspec_setup.rb +21 -0
- data/lib/adequack/proxy.rb +86 -0
- data/lib/adequack/version.rb +3 -0
- data/lib/adequack.rb +16 -0
- data/spec/adequack_spec.rb +129 -0
- data/spec/integration_spec.rb +64 -0
- data/spec/spec_helper.rb +1 -0
- metadata +104 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6cbd122c01215eb0d14f941fc68ecb1f147bfe3b
|
4
|
+
data.tar.gz: 1dbcf7bc56ae09478d86f0ae47b6fe4f4db6040d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 93f3f69ffbfaf2c76ca608bcc3bf4d1c51e652f601e203d08f59b601dbe2d86b11669b55237cda7671dd800783d262ce28bde0df55fc66834d6750ce4e7ec49a
|
7
|
+
data.tar.gz: 13c964cd0d085001f92330d13468b149c49c2a820f756ca4ff2e571aaf369cdddad0057b41fe7266ae811129aac7e81b43f6386173b4e2540fc511f46fd6e75b
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Ilya Zayats
|
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,229 @@
|
|
1
|
+
# Adequack
|
2
|
+
|
3
|
+
Everyone likes isolation testing now. And when you do it then you stubbing and mocking a lot.
|
4
|
+
But the main concern when you use this approach is that you stubs will be out of sync with the
|
5
|
+
real objects.
|
6
|
+
Adequack addresses this issue.
|
7
|
+
|
8
|
+
## Problem 1. Missing methods
|
9
|
+
|
10
|
+
Let's dive into this toy code:
|
11
|
+
```ruby
|
12
|
+
class Dog
|
13
|
+
def eat_food
|
14
|
+
puts "delicious!"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Owner
|
19
|
+
def initialize(dog)
|
20
|
+
@dog = dog
|
21
|
+
end
|
22
|
+
|
23
|
+
def feed_animal
|
24
|
+
@dog.eat_food
|
25
|
+
end
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
and we are going to spec things out:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
describe Owner do
|
33
|
+
let(:dog) { double }
|
34
|
+
subject { described_class.new dog }
|
35
|
+
|
36
|
+
it "feeds animal" do
|
37
|
+
dog.should_receive(:eat_food)
|
38
|
+
subject.feed_animal
|
39
|
+
end
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
And everything will pass.
|
44
|
+
|
45
|
+
But let's imagine that a big dog food brand will come to us and pay a lot for branding:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
class Dog
|
49
|
+
def eat_chappi
|
50
|
+
puts "delicious chappi dog food! I will recommend it to all my buddies!"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
So we've changed our code and we should run the tests:
|
56
|
+
|
57
|
+
```
|
58
|
+
.
|
59
|
+
|
60
|
+
Finished in 0.00051 seconds
|
61
|
+
1 example, 0 failures
|
62
|
+
```
|
63
|
+
|
64
|
+
You are very confident about your test suite and you decide to deploy the changes to production.
|
65
|
+
After that big dog food brand will take that payment away because of:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
undefined method `eat_food' for #<Dog:0x99250dc>
|
69
|
+
```
|
70
|
+
|
71
|
+
### Solving this with adequack
|
72
|
+
|
73
|
+
Let's replay the story again but with a happy end:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
require 'adequack'
|
77
|
+
|
78
|
+
module DogInterface
|
79
|
+
def eat_food; end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe Dog do
|
83
|
+
subject { described_class }
|
84
|
+
it { should be_adequack_to DogInterface }
|
85
|
+
end
|
86
|
+
|
87
|
+
describe Owner do
|
88
|
+
let(:dog) { adequack_double double, DogInterface }
|
89
|
+
subject { described_class.new dog }
|
90
|
+
|
91
|
+
it "feeds animal" do
|
92
|
+
dog.should_receive(:eat_food)
|
93
|
+
subject.feed_animal
|
94
|
+
end
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
and we will have 2 passing dots:
|
99
|
+
|
100
|
+
```
|
101
|
+
.
|
102
|
+
|
103
|
+
Finished in 0.00128 seconds
|
104
|
+
2 examples, 0 failures
|
105
|
+
```
|
106
|
+
|
107
|
+
We should validate not only our mocks, but also that our core object really responds to the interface.
|
108
|
+
Use `be_adequack_to` matcher with a core class as an argument.
|
109
|
+
|
110
|
+
And to create doubles and stubs use `adequack_double` helper. This will return a proxy object that
|
111
|
+
will translate all calls to the object that you'll pass first (plain `double` at the example).
|
112
|
+
|
113
|
+
Let's replay our changes again:
|
114
|
+
```ruby
|
115
|
+
class Dog
|
116
|
+
def eat_chappi
|
117
|
+
puts "delicious chappi dog food! I will recommend it to all my buddies!"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
```
|
121
|
+
|
122
|
+
and run our specs:
|
123
|
+
```
|
124
|
+
F.
|
125
|
+
|
126
|
+
Failures:
|
127
|
+
|
128
|
+
1) Dog
|
129
|
+
Failure/Error: it { should be_adequack_to DogInterface }
|
130
|
+
Adequack::InterfaceImplementationError:
|
131
|
+
object does not respond to 'eat_food' method
|
132
|
+
|
133
|
+
```
|
134
|
+
|
135
|
+
Here we gon an error at the dog spec, because our core object falls out of sync with our interface.
|
136
|
+
But big sponsor is paying, so we will change the interface too:
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
module DogInterface
|
140
|
+
def eat_chappi; end
|
141
|
+
end
|
142
|
+
```
|
143
|
+
|
144
|
+
and rerun our tests:
|
145
|
+
```
|
146
|
+
.F
|
147
|
+
|
148
|
+
Failures:
|
149
|
+
|
150
|
+
1) Owner feeds animal
|
151
|
+
Failure/Error: dog.should_receive(:eat_food)
|
152
|
+
Adequack::InterfaceImplementationError:
|
153
|
+
trying to stub nonexistent method
|
154
|
+
|
155
|
+
```
|
156
|
+
|
157
|
+
Another failure, we should change our stubs too!
|
158
|
+
|
159
|
+
And at this time we will ger our payment fully.
|
160
|
+
|
161
|
+
## Problem 2. Method signatures
|
162
|
+
|
163
|
+
But what if method is there, but signature is changed?
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
class Dog
|
167
|
+
def eat_food(brand)
|
168
|
+
puts "delicious #{brand} dog food! I will recommend it to all my buddies!"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
```
|
172
|
+
|
173
|
+
and our specs will tell you about that:
|
174
|
+
|
175
|
+
```
|
176
|
+
.F
|
177
|
+
|
178
|
+
Failures:
|
179
|
+
|
180
|
+
1) Owner feeds animal
|
181
|
+
Failure/Error: @dog.eat_food
|
182
|
+
Adequack::InterfaceImplementationError:
|
183
|
+
definition of method 'eat_food' differs in parameters accepted.
|
184
|
+
```
|
185
|
+
|
186
|
+
## Installation
|
187
|
+
|
188
|
+
Just install the gem
|
189
|
+
```
|
190
|
+
gem install adequack
|
191
|
+
```
|
192
|
+
|
193
|
+
and require it when you need it: `require 'adequack'`
|
194
|
+
|
195
|
+
After that your rspec tests will have `be_adequack_to` matcher and `adequack_double` helper.
|
196
|
+
|
197
|
+
## Usage
|
198
|
+
|
199
|
+
TODO: Write full API definition here
|
200
|
+
|
201
|
+
## Contributing
|
202
|
+
|
203
|
+
This library is considered "experimental" quality.
|
204
|
+
Your feedback would be very welcome! Pull requests are great, but issues are good too.
|
205
|
+
|
206
|
+
## Licence
|
207
|
+
|
208
|
+
Copyright (c) 2013 Ilya Zayats
|
209
|
+
|
210
|
+
MIT License
|
211
|
+
|
212
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
213
|
+
a copy of this software and associated documentation files (the
|
214
|
+
"Software"), to deal in the Software without restriction, including
|
215
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
216
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
217
|
+
permit persons to whom the Software is furnished to do so, subject to
|
218
|
+
the following conditions:
|
219
|
+
|
220
|
+
The above copyright notice and this permission notice shall be
|
221
|
+
included in all copies or substantial portions of the Software.
|
222
|
+
|
223
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
224
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
225
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
226
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
227
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
228
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
229
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
data/adequack.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'adequack/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "adequack"
|
8
|
+
spec.version = Adequack::VERSION
|
9
|
+
spec.authors = ["Ilya Zayats"]
|
10
|
+
spec.email = ["somebody32@gmail.com"]
|
11
|
+
spec.description = %q{Be sure that your mocks are adequate}
|
12
|
+
spec.summary = %q{Be sure that your mocks are adequate}
|
13
|
+
spec.homepage = "https://github.com/Somebody32/adequack"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "rspec", "~> 2.13"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Adequack
|
2
|
+
class Core
|
3
|
+
|
4
|
+
def self.implements(object, interface)
|
5
|
+
new(object, interface).send(:validate_ducktype)
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def initialize(duck, interface)
|
11
|
+
self.duck = duck
|
12
|
+
self.interface = interface
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate_ducktype
|
16
|
+
check_method_implementation(get_methods interface.public_methods )
|
17
|
+
check_method_implementation(
|
18
|
+
get_methods(interface.public_instance_methods), true
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def check_method_implementation(methods, instance = false)
|
23
|
+
methods.each do |method|
|
24
|
+
if instance
|
25
|
+
name = method
|
26
|
+
defined = duck.method_defined?(method)
|
27
|
+
else
|
28
|
+
name = "self.#{method}"
|
29
|
+
defined = duck.respond_to?(method)
|
30
|
+
end
|
31
|
+
|
32
|
+
raise InterfaceImplementationError,
|
33
|
+
"object does not respond to '#{name}' method" unless defined
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_accessor :duck, :interface
|
38
|
+
|
39
|
+
def get_methods(methods)
|
40
|
+
methods - Object.methods
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Adequack
|
2
|
+
module Integration
|
3
|
+
module RSpecHelpers
|
4
|
+
def adequack_double(object, interface)
|
5
|
+
Adequack.double object, interface
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
RSpec::Matchers.define :be_adequack_to do |*expected_duck_types|
|
12
|
+
expected_duck_types.each do |expected_duck_type|
|
13
|
+
match do |actual|
|
14
|
+
Adequack.check_implementation(actual, expected_duck_type)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
RSpec.configure do |config|
|
20
|
+
config.include Adequack::Integration::RSpecHelpers
|
21
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Adequack
|
2
|
+
class Proxy
|
3
|
+
|
4
|
+
def initialize(target, interface)
|
5
|
+
self.target = target
|
6
|
+
self.interface = interface
|
7
|
+
end
|
8
|
+
|
9
|
+
def stub(message_or_hash, opts = {}, &block)
|
10
|
+
methods =
|
11
|
+
Hash === message_or_hash ? message_or_hash.keys : [message_or_hash]
|
12
|
+
|
13
|
+
methods.each { |m| check_method_existence m }
|
14
|
+
|
15
|
+
target.stub(message_or_hash, opts, &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
alias_method :stub!, :stub
|
19
|
+
|
20
|
+
def stub_chain(*chain, &blk)
|
21
|
+
method =
|
22
|
+
String === chain.first ? chain.first.split(".").first : chain.first
|
23
|
+
|
24
|
+
check_method_existence method
|
25
|
+
|
26
|
+
target.stub_chain(*chain, &blk)
|
27
|
+
end
|
28
|
+
|
29
|
+
def should_receive(message, opts = {}, &block)
|
30
|
+
check_method_existence message
|
31
|
+
|
32
|
+
target.should_receive(message, opts, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
attr_accessor :target, :interface
|
38
|
+
|
39
|
+
def method_missing(name, *args, &block)
|
40
|
+
check_interface_implementation name, args
|
41
|
+
target.send name, *args, &block
|
42
|
+
end
|
43
|
+
|
44
|
+
def check_interface_implementation(name, args)
|
45
|
+
check_interface_signature(name, args) if method_in_interface?(name)
|
46
|
+
end
|
47
|
+
|
48
|
+
def check_interface_signature(name, args)
|
49
|
+
target_method = duck_type_methods.select { |m| m.name == name }.first
|
50
|
+
req_m = target_method.parameters.select { |m| m.first == :req }
|
51
|
+
|
52
|
+
if args.size < req_m.size
|
53
|
+
raise InterfaceImplementationError,
|
54
|
+
"definition of method '#{name}' differs in parameters accepted."
|
55
|
+
end
|
56
|
+
|
57
|
+
unless target_method.parameters.any? { |m| m.first == :rest }
|
58
|
+
opt_m = target_method.parameters.select { |m| m.first == :opt }
|
59
|
+
|
60
|
+
if args.size > (req_m.size + opt_m.size)
|
61
|
+
raise InterfaceImplementationError,
|
62
|
+
"definition of method '#{name}' differs in parameters accepted."
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def check_method_existence(method)
|
68
|
+
unless method_in_interface? method
|
69
|
+
raise InterfaceImplementationError,
|
70
|
+
"trying to stub nonexistent method"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def method_in_interface?(method)
|
75
|
+
duck_type_methods.map(&:name).include? method.to_sym
|
76
|
+
end
|
77
|
+
|
78
|
+
def duck_type_methods
|
79
|
+
@duck_type_methods ||= (interface.instance_methods - Object.methods)
|
80
|
+
@duck_type_methods.map do |method_name|
|
81
|
+
interface.public_instance_method(method_name)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
data/lib/adequack.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "adequack/version"
|
2
|
+
require "adequack/core"
|
3
|
+
require "adequack/proxy"
|
4
|
+
require "adequack/integration/rspec_setup"
|
5
|
+
|
6
|
+
module Adequack
|
7
|
+
InterfaceImplementationError = Class.new(::StandardError)
|
8
|
+
|
9
|
+
def self.check_implementation(duck, interface)
|
10
|
+
Core.implements duck, interface
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.double(core, interface)
|
14
|
+
Proxy.new core, interface
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Adequack do
|
4
|
+
|
5
|
+
let!(:interface) do
|
6
|
+
Class.new do
|
7
|
+
def self.evolutionize; end
|
8
|
+
def bark(what); end
|
9
|
+
def eat(tasty = true); end
|
10
|
+
def drink(what, many = true); end
|
11
|
+
def sleep(how_long, *places); end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
before do
|
16
|
+
Object.send(:remove_const, :Animal) if Object.const_defined?(:Animal)
|
17
|
+
end
|
18
|
+
|
19
|
+
context "when checking interface implementation" do
|
20
|
+
|
21
|
+
it "passes when test class is ok" do
|
22
|
+
class Animal
|
23
|
+
def self.evolutionize; end
|
24
|
+
def bark(what); end
|
25
|
+
def eat(tasty = true); end
|
26
|
+
def drink(what, many = true); end
|
27
|
+
def sleep(how_long, *places); end
|
28
|
+
end
|
29
|
+
|
30
|
+
expect { described_class.check_implementation(Animal, interface) }.
|
31
|
+
not_to raise_error Adequack::InterfaceImplementationError
|
32
|
+
end
|
33
|
+
|
34
|
+
it "fails when no class method" do
|
35
|
+
class Animal
|
36
|
+
def bark(what); end
|
37
|
+
def eat(tasty = true); end
|
38
|
+
def drink(what, many = true); end
|
39
|
+
def sleep(how_long, *places); end
|
40
|
+
end
|
41
|
+
|
42
|
+
expect { described_class.check_implementation(Animal, interface) }.
|
43
|
+
to raise_error Adequack::InterfaceImplementationError,
|
44
|
+
"object does not respond to 'self.evolutionize' method"
|
45
|
+
end
|
46
|
+
|
47
|
+
it "will not take care about private and protected methods" do
|
48
|
+
class Animal
|
49
|
+
def bark(what); end
|
50
|
+
end
|
51
|
+
|
52
|
+
bad_interface = Class.new do
|
53
|
+
def bark(what); end
|
54
|
+
|
55
|
+
class << self
|
56
|
+
protected
|
57
|
+
def c_a; end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.c_b; end
|
61
|
+
private_class_method :c_b
|
62
|
+
|
63
|
+
protected
|
64
|
+
def a; end
|
65
|
+
|
66
|
+
private
|
67
|
+
def b; end
|
68
|
+
end
|
69
|
+
|
70
|
+
expect { described_class.check_implementation(Animal, bad_interface) }.
|
71
|
+
not_to raise_error Adequack::InterfaceImplementationError
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when stubbing and mocking" do
|
77
|
+
|
78
|
+
let(:core) { double }
|
79
|
+
let(:subject) { Adequack.double core, interface }
|
80
|
+
|
81
|
+
it "let you stub methods that exists and return actual object" do
|
82
|
+
core.should_receive(:stub).with({ bark: "woof" }, {})
|
83
|
+
subject.stub(bark: "woof")
|
84
|
+
end
|
85
|
+
|
86
|
+
it "raises an error if trying to stub nonexistent method via hash" do
|
87
|
+
expect { subject.stub(muuuu: "hey hoo") }.
|
88
|
+
to raise_error Adequack::InterfaceImplementationError,
|
89
|
+
"trying to stub nonexistent method"
|
90
|
+
end
|
91
|
+
|
92
|
+
it "raises an error if trying to stub nonexistent method via message" do
|
93
|
+
expect { subject.stub(:muuuu) }.
|
94
|
+
to raise_error Adequack::InterfaceImplementationError,
|
95
|
+
"trying to stub nonexistent method"
|
96
|
+
end
|
97
|
+
|
98
|
+
it "works with chaining" do
|
99
|
+
core.should_receive(:stub_chain).with("bark.words")
|
100
|
+
subject.stub_chain("bark.words")
|
101
|
+
end
|
102
|
+
|
103
|
+
it "raises if chain in symbol form" do
|
104
|
+
expect { subject.stub_chain(:tongue, :skin) }.
|
105
|
+
to raise_error Adequack::InterfaceImplementationError,
|
106
|
+
"trying to stub nonexistent method"
|
107
|
+
end
|
108
|
+
|
109
|
+
it "raises if chain in string form" do
|
110
|
+
expect { subject.stub_chain("tongue.skin") }.
|
111
|
+
to raise_error Adequack::InterfaceImplementationError,
|
112
|
+
"trying to stub nonexistent method"
|
113
|
+
end
|
114
|
+
|
115
|
+
it "works with should receive too" do
|
116
|
+
expect { subject.should_receive(:muuuu) }.
|
117
|
+
to raise_error Adequack::InterfaceImplementationError,
|
118
|
+
"trying to stub nonexistent method"
|
119
|
+
end
|
120
|
+
|
121
|
+
it "tell you if you'r passing invalid arguments" do
|
122
|
+
expect {
|
123
|
+
core.should_not_receive(:bark)
|
124
|
+
subject.bark("woof", "not woof")
|
125
|
+
}.to raise_error Adequack::InterfaceImplementationError,
|
126
|
+
"definition of method 'bark' differs in parameters accepted."
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module AnimalInterface
|
4
|
+
def self.evolutionize(from_what); end
|
5
|
+
def bark(what); end
|
6
|
+
def feed(what, many = false); end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Animal
|
10
|
+
def initialize(name)
|
11
|
+
@name = name
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.evolutionize(from_what)
|
15
|
+
new("next step from #{from_what}")
|
16
|
+
end
|
17
|
+
|
18
|
+
def bark(what)
|
19
|
+
puts what
|
20
|
+
end
|
21
|
+
|
22
|
+
def feed(what, many = false)
|
23
|
+
emotion = "So yammy #{what}"
|
24
|
+
emotion = [emotion, "I'm full up"].join(", ") if many
|
25
|
+
|
26
|
+
bark emotion
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe Animal do
|
31
|
+
subject { described_class }
|
32
|
+
it { should be_adequack_to AnimalInterface }
|
33
|
+
end
|
34
|
+
|
35
|
+
class Owner
|
36
|
+
|
37
|
+
def initialize(animal)
|
38
|
+
@animal = animal
|
39
|
+
end
|
40
|
+
|
41
|
+
def trick_animal
|
42
|
+
@animal.bark("woof")
|
43
|
+
end
|
44
|
+
|
45
|
+
def enormously_feed_animal
|
46
|
+
@animal.feed("chappy", true)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
describe Owner do
|
52
|
+
let(:animal) { adequack_double double, AnimalInterface }
|
53
|
+
subject { described_class.new animal }
|
54
|
+
|
55
|
+
it "tricks animal" do
|
56
|
+
animal.should_receive(:bark).and_return("barked")
|
57
|
+
expect(subject.trick_animal).to eql "barked"
|
58
|
+
end
|
59
|
+
|
60
|
+
it "feeds animal" do
|
61
|
+
animal.should_receive(:feed).and_return("barked")
|
62
|
+
expect(subject.enormously_feed_animal).to eql "barked"
|
63
|
+
end
|
64
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "adequack"
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: adequack
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ilya Zayats
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-04-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.13'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.13'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Be sure that your mocks are adequate
|
56
|
+
email:
|
57
|
+
- somebody32@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- .gitignore
|
63
|
+
- .rspec
|
64
|
+
- Gemfile
|
65
|
+
- LICENSE.txt
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- adequack.gemspec
|
69
|
+
- lib/adequack.rb
|
70
|
+
- lib/adequack/core.rb
|
71
|
+
- lib/adequack/integration/rspec_setup.rb
|
72
|
+
- lib/adequack/proxy.rb
|
73
|
+
- lib/adequack/version.rb
|
74
|
+
- spec/adequack_spec.rb
|
75
|
+
- spec/integration_spec.rb
|
76
|
+
- spec/spec_helper.rb
|
77
|
+
homepage: https://github.com/Somebody32/adequack
|
78
|
+
licenses:
|
79
|
+
- MIT
|
80
|
+
metadata: {}
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 2.0.3
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: Be sure that your mocks are adequate
|
101
|
+
test_files:
|
102
|
+
- spec/adequack_spec.rb
|
103
|
+
- spec/integration_spec.rb
|
104
|
+
- spec/spec_helper.rb
|