verified_double 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.markdown +12 -0
- data/README.md +25 -14
- data/features/CHANGELOG.markdown +12 -0
- data/features/readme.md +25 -14
- data/features/verified_mocks.feature +4 -2
- data/lib/verified_double.rb +10 -1
- data/lib/verified_double/matchers.rb +2 -2
- data/lib/verified_double/method_signature.rb +5 -5
- data/lib/verified_double/method_signature/boolean_value.rb +9 -0
- data/lib/verified_double/method_signature/class_value.rb +17 -0
- data/lib/verified_double/method_signature/instance_double_value.rb +9 -0
- data/lib/verified_double/method_signature/instance_value.rb +17 -0
- data/lib/verified_double/method_signature/recording_double_class_value.rb +9 -0
- data/lib/verified_double/method_signature/rspec_double_value.rb +9 -0
- data/lib/verified_double/method_signature/value.rb +37 -0
- data/lib/verified_double/method_signatures_report.rb +10 -3
- data/lib/verified_double/parse_method_signature.rb +2 -2
- data/lib/verified_double/recorded_method_signature.rb +9 -0
- data/lib/verified_double/recording_double.rb +13 -4
- data/lib/verified_double/stack_frame.rb +11 -0
- data/lib/verified_double/version.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/verified_double/matchers_spec.rb +1 -1
- data/spec/verified_double/method_signature/boolean_value_spec.rb +11 -0
- data/spec/verified_double/method_signature/class_value_spec.rb +35 -0
- data/spec/verified_double/method_signature/instance_double_value_spec.rb +17 -0
- data/spec/verified_double/method_signature/instance_value_spec.rb +39 -0
- data/spec/verified_double/method_signature/recording_double_class_value_spec.rb +23 -0
- data/spec/verified_double/method_signature/rspec_double_spec.rb +14 -0
- data/spec/verified_double/method_signature/value_spec.rb +89 -0
- data/spec/verified_double/method_signature_spec.rb +16 -16
- data/spec/verified_double/method_signatures_report_spec.rb +14 -12
- data/spec/verified_double/parse_method_signature_spec.rb +3 -3
- data/spec/verified_double/recorded_method_signature_spec.rb +23 -0
- data/spec/verified_double/recording_double_spec.rb +103 -77
- data/spec/verified_double/stack_frame_spec.rb +15 -0
- data/spec/verified_double_spec.rb +1 -1
- metadata +29 -7
- data/lib/verified_double/method_signature_value.rb +0 -45
- data/spec/unit_helper.rb +0 -16
- data/spec/verified_double/method_signature_value_spec.rb +0 -126
data/CHANGELOG.markdown
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
0.2.0 - 2013-06-30
|
2
|
+
------------------
|
3
|
+
|
4
|
+
* [#4] Add stack frames and relish app link to output.
|
5
|
+
* [#12] RecordingDouble#class should be the class of the object being doubled.
|
6
|
+
* [#16] Fix handling of doubles passed as method signature values.
|
7
|
+
|
8
|
+
Known issues:
|
9
|
+
|
10
|
+
* Stack frames sometimes point to verified_double gem instead of where the mock was recorded.
|
11
|
+
|
12
|
+
|
1
13
|
0.1.1 - 2013-06-26
|
2
14
|
------------------
|
3
15
|
|
data/README.md
CHANGED
@@ -2,34 +2,45 @@
|
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/gsmendoza/verified_double.png)](https://travis-ci.org/gsmendoza/verified_double)
|
4
4
|
|
5
|
-
VerifiedDouble
|
5
|
+
VerifiedDouble is a gem for verifying rspec mocks. The gem works similar to [rspec-fire](https://github.com/xaviershay/rspec-fire). However, instead of checking if the doubled classes respond to the methods, the gem looks for tests confirming if those mocks are valid.
|
6
6
|
|
7
7
|
For example, let's say I mocked the created_at method of a model like this:
|
8
8
|
|
9
9
|
item = VerifiedDouble.of_instance(Item)
|
10
10
|
item.should_receive(:created_at).and_return(Time.now)
|
11
11
|
|
12
|
-
|
13
|
-
running the tests, the gem looks for a test that confirms if you can
|
14
|
-
indeed call #created_at on Item. The test should also ensure that the method
|
15
|
-
returns a Time object. Since this is hard to automate, the gem just looks
|
16
|
-
for a test with a tag saying it verifies that contract:
|
12
|
+
When running the tests, the gem looks for a "contract test" tagged with the method's signature. This test should ensure that calling #created_at on Item will return a Time object.
|
17
13
|
|
18
14
|
it "tests something", verifies_contract: 'Item#created_at()=>Time' do
|
19
15
|
#...
|
20
16
|
end
|
21
17
|
|
22
|
-
If this test does not exist, the gem will complain that the mock is not
|
23
|
-
verified.
|
18
|
+
If this test does not exist, the gem will complain that the mock is not verified.
|
24
19
|
|
25
|
-
|
20
|
+
I got the idea from http://www.infoq.com/presentations/integration-tests-scam, an old (2009) talk that still has some fresh insights on dealing with API changes in your mocked tests.
|
26
21
|
|
27
|
-
|
28
|
-
----------
|
22
|
+
You can learn more about using the gem at https://www.relishapp.com/gsmendoza/verified-double.
|
29
23
|
|
30
|
-
|
31
|
-
|
32
|
-
|
24
|
+
Actively tested against
|
25
|
+
-----------------------
|
26
|
+
|
27
|
+
* Ruby 1.9.3
|
28
|
+
* RSpec 2.13
|
29
|
+
|
30
|
+
|
31
|
+
Alternatives
|
32
|
+
------------
|
33
|
+
|
34
|
+
[Bogus](https://www.relishapp.com/bogus/bogus/v/0-0-3/docs/) is the first gem to implement contract tests. It doesn't rely on rspec tags to verify contracts, so it's probably a lot smarter than VerifiedDouble :) However, I wasn't able to try it out on my own projects because of its own rr-like mock adapter. But do check it out!
|
35
|
+
|
36
|
+
Caveats
|
37
|
+
-------
|
38
|
+
|
39
|
+
VerifiedDouble is still in its infancy, but I hope it's usable for the most common cases.
|
40
|
+
|
41
|
+
* If you check the RelishApp doc, the gem only supports a subset of rspec-mock's API. Please post an issue at http://github.com/gsmendoza/verified_double if you need support for any particular rspec-mock API.
|
42
|
+
|
43
|
+
* The [method documentation](http://rubydoc.info/gems/verified_double) is pretty empty at this point :p I'm planning to use yard-spec to document the methods but that gem doesn't support rspec context blocks. I'll try to work on that soon.
|
33
44
|
|
34
45
|
Special thanks
|
35
46
|
--------------
|
data/features/CHANGELOG.markdown
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
0.2.0 - 2013-06-30
|
2
|
+
------------------
|
3
|
+
|
4
|
+
* [#4] Add stack frames and relish app link to output.
|
5
|
+
* [#12] RecordingDouble#class should be the class of the object being doubled.
|
6
|
+
* [#16] Fix handling of doubles passed as method signature values.
|
7
|
+
|
8
|
+
Known issues:
|
9
|
+
|
10
|
+
* Stack frames sometimes point to verified_double gem instead of where the mock was recorded.
|
11
|
+
|
12
|
+
|
1
13
|
0.1.1 - 2013-06-26
|
2
14
|
------------------
|
3
15
|
|
data/features/readme.md
CHANGED
@@ -2,34 +2,45 @@
|
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/gsmendoza/verified_double.png)](https://travis-ci.org/gsmendoza/verified_double)
|
4
4
|
|
5
|
-
VerifiedDouble
|
5
|
+
VerifiedDouble is a gem for verifying rspec mocks. The gem works similar to [rspec-fire](https://github.com/xaviershay/rspec-fire). However, instead of checking if the doubled classes respond to the methods, the gem looks for tests confirming if those mocks are valid.
|
6
6
|
|
7
7
|
For example, let's say I mocked the created_at method of a model like this:
|
8
8
|
|
9
9
|
item = VerifiedDouble.of_instance(Item)
|
10
10
|
item.should_receive(:created_at).and_return(Time.now)
|
11
11
|
|
12
|
-
|
13
|
-
running the tests, the gem looks for a test that confirms if you can
|
14
|
-
indeed call #created_at on Item. The test should also ensure that the method
|
15
|
-
returns a Time object. Since this is hard to automate, the gem just looks
|
16
|
-
for a test with a tag saying it verifies that contract:
|
12
|
+
When running the tests, the gem looks for a "contract test" tagged with the method's signature. This test should ensure that calling #created_at on Item will return a Time object.
|
17
13
|
|
18
14
|
it "tests something", verifies_contract: 'Item#created_at()=>Time' do
|
19
15
|
#...
|
20
16
|
end
|
21
17
|
|
22
|
-
If this test does not exist, the gem will complain that the mock is not
|
23
|
-
verified.
|
18
|
+
If this test does not exist, the gem will complain that the mock is not verified.
|
24
19
|
|
25
|
-
|
20
|
+
I got the idea from http://www.infoq.com/presentations/integration-tests-scam, an old (2009) talk that still has some fresh insights on dealing with API changes in your mocked tests.
|
26
21
|
|
27
|
-
|
28
|
-
----------
|
22
|
+
You can learn more about using the gem at https://www.relishapp.com/gsmendoza/verified-double.
|
29
23
|
|
30
|
-
|
31
|
-
|
32
|
-
|
24
|
+
Actively tested against
|
25
|
+
-----------------------
|
26
|
+
|
27
|
+
* Ruby 1.9.3
|
28
|
+
* RSpec 2.13
|
29
|
+
|
30
|
+
|
31
|
+
Alternatives
|
32
|
+
------------
|
33
|
+
|
34
|
+
[Bogus](https://www.relishapp.com/bogus/bogus/v/0-0-3/docs/) is the first gem to implement contract tests. It doesn't rely on rspec tags to verify contracts, so it's probably a lot smarter than VerifiedDouble :) However, I wasn't able to try it out on my own projects because of its own rr-like mock adapter. But do check it out!
|
35
|
+
|
36
|
+
Caveats
|
37
|
+
-------
|
38
|
+
|
39
|
+
VerifiedDouble is still in its infancy, but I hope it's usable for the most common cases.
|
40
|
+
|
41
|
+
* If you check the RelishApp doc, the gem only supports a subset of rspec-mock's API. Please post an issue at http://github.com/gsmendoza/verified_double if you need support for any particular rspec-mock API.
|
42
|
+
|
43
|
+
* The [method documentation](http://rubydoc.info/gems/verified_double) is pretty empty at this point :p I'm planning to use yard-spec to document the methods but that gem doesn't support rspec context blocks. I'll try to work on that soon.
|
33
44
|
|
34
45
|
Special thanks
|
35
46
|
--------------
|
@@ -93,7 +93,8 @@ Feature: 01. Verified mocks
|
|
93
93
|
Then I should be informed that the mock is unverified:
|
94
94
|
"""
|
95
95
|
The following mocks are not verified:
|
96
|
-
|
96
|
+
|
97
|
+
1. Collaborator#some_method(SomeInput)=>SomeOutput
|
97
98
|
"""
|
98
99
|
|
99
100
|
Scenario: Verified class doubles
|
@@ -147,5 +148,6 @@ Feature: 01. Verified mocks
|
|
147
148
|
Then I should be informed that the mock is unverified:
|
148
149
|
"""
|
149
150
|
The following mocks are not verified:
|
150
|
-
|
151
|
+
|
152
|
+
1. Collaborator.some_method(SomeInput)=>SomeOutput
|
151
153
|
"""
|
data/lib/verified_double.rb
CHANGED
@@ -1,13 +1,22 @@
|
|
1
|
+
require 'active_support/core_ext/string'
|
1
2
|
require 'rspec/mocks'
|
2
3
|
|
3
4
|
require 'verified_double/boolean'
|
4
5
|
require 'verified_double/matchers'
|
5
6
|
require 'verified_double/method_signature'
|
6
|
-
require 'verified_double/
|
7
|
+
require 'verified_double/method_signature/value'
|
8
|
+
require 'verified_double/method_signature/instance_value'
|
9
|
+
require 'verified_double/method_signature/boolean_value'
|
10
|
+
require 'verified_double/method_signature/class_value'
|
11
|
+
require 'verified_double/method_signature/instance_double_value'
|
12
|
+
require 'verified_double/method_signature/recording_double_class_value'
|
13
|
+
require 'verified_double/method_signature/rspec_double_value'
|
7
14
|
require 'verified_double/method_signatures_report'
|
8
15
|
require 'verified_double/parse_method_signature'
|
16
|
+
require 'verified_double/recorded_method_signature'
|
9
17
|
require 'verified_double/recording_double'
|
10
18
|
require 'verified_double/relays_to_internal_double_returning_self'
|
19
|
+
require 'verified_double/stack_frame'
|
11
20
|
|
12
21
|
module VerifiedDouble
|
13
22
|
extend RSpec::Mocks::ExampleMethods
|
@@ -12,7 +12,7 @@ module VerifiedDouble
|
|
12
12
|
|
13
13
|
raise CannotHandleMultipleReturnValues if method_signature.return_values.size > 1
|
14
14
|
|
15
|
-
value = method_signature.return_values.first.
|
15
|
+
value = method_signature.return_values.first.content_as_instance
|
16
16
|
actual.send "#{method_signature.method}=", value
|
17
17
|
actual.send(method_signature.method) == value
|
18
18
|
end
|
@@ -26,7 +26,7 @@ module VerifiedDouble
|
|
26
26
|
|
27
27
|
raise CannotHandleMultipleReturnValues if method_signature.return_values.size > 1
|
28
28
|
|
29
|
-
actual.send(method_signature.method).is_a?(method_signature.return_values.first.
|
29
|
+
actual.send(method_signature.method).is_a?(method_signature.return_values.first.content)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
@@ -8,14 +8,14 @@ module VerifiedDouble
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
11
|
+
def belongs_to?(other)
|
12
12
|
self.class_name == other.class_name &&
|
13
13
|
self.method_operator == other.method_operator &&
|
14
14
|
self.method == other.method &&
|
15
15
|
self.args.size == other.args.size &&
|
16
|
-
(0 ... args.size).all?{|i| self.args[i].
|
16
|
+
(0 ... args.size).all?{|i| self.args[i].belongs_to?(other.args[i]) } &&
|
17
17
|
self.return_values.size == other.return_values.size &&
|
18
|
-
(0 ... return_values.size).all?{|i| self.return_values[i].
|
18
|
+
(0 ... return_values.size).all?{|i| self.return_values[i].belongs_to?(other.return_values[i]) }
|
19
19
|
end
|
20
20
|
|
21
21
|
def args
|
@@ -42,8 +42,8 @@ module VerifiedDouble
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def to_s
|
45
|
-
args_string = args.map(&:
|
46
|
-
return_values_string = return_values.map(&:
|
45
|
+
args_string = args.map(&:content).map{|v| v || 'nil'}.join(', ')
|
46
|
+
return_values_string = return_values.map(&:content).map{|v| v || 'nil'}.join(', ')
|
47
47
|
return_values_string = nil if return_values_string.empty?
|
48
48
|
|
49
49
|
result = [
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module VerifiedDouble
|
2
|
+
class MethodSignature
|
3
|
+
class ClassValue < Value
|
4
|
+
def belongs_to?(other)
|
5
|
+
self.content == other.content
|
6
|
+
end
|
7
|
+
|
8
|
+
def content_as_instance
|
9
|
+
begin
|
10
|
+
content.new
|
11
|
+
rescue NoMethodError
|
12
|
+
Object.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module VerifiedDouble
|
2
|
+
class MethodSignature
|
3
|
+
class InstanceValue < Value
|
4
|
+
def belongs_to?(other)
|
5
|
+
if other.is_a?(MethodSignature::InstanceValue)
|
6
|
+
self.content == other.content
|
7
|
+
else
|
8
|
+
self.content_class.ancestors.include?(other.content)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def content_as_instance
|
13
|
+
self.content
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module VerifiedDouble
|
2
|
+
class MethodSignature
|
3
|
+
class Value
|
4
|
+
attr_reader :content
|
5
|
+
|
6
|
+
def self.from(content)
|
7
|
+
if content == true || content == false
|
8
|
+
BooleanValue.new(content)
|
9
|
+
elsif content.is_a?(RSpec::Mocks::Mock)
|
10
|
+
RspecDoubleValue.new(content)
|
11
|
+
elsif content.respond_to?(:doubled_class)
|
12
|
+
if content.class_double?
|
13
|
+
RecordingDoubleClassValue.new(content)
|
14
|
+
else
|
15
|
+
InstanceDoubleValue.new(content)
|
16
|
+
end
|
17
|
+
elsif content.is_a?(Module)
|
18
|
+
ClassValue.new(content)
|
19
|
+
else
|
20
|
+
InstanceValue.new(content)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(content)
|
25
|
+
@content = content
|
26
|
+
end
|
27
|
+
|
28
|
+
def content_class
|
29
|
+
content.class
|
30
|
+
end
|
31
|
+
|
32
|
+
def recommended_value
|
33
|
+
MethodSignature::Value.from(self.content_class)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -14,7 +14,7 @@ module VerifiedDouble
|
|
14
14
|
def identify_unverified_signatures
|
15
15
|
@unverified_signatures = @registered_signatures.select{|registered_signature|
|
16
16
|
@verified_signatures.all?{|verified_signature|
|
17
|
-
! registered_signature.
|
17
|
+
! registered_signature.belongs_to?(verified_signature) } }
|
18
18
|
self
|
19
19
|
end
|
20
20
|
|
@@ -25,8 +25,15 @@ module VerifiedDouble
|
|
25
25
|
|
26
26
|
def output_unverified_signatures
|
27
27
|
if @unverified_signatures.any?
|
28
|
-
output = ["The following mocks are not verified:" ] +
|
29
|
-
|
28
|
+
output = [nil, "The following mocks are not verified:" ] +
|
29
|
+
@unverified_signatures
|
30
|
+
.map(&:recommended_verified_signature)
|
31
|
+
.map(&:to_s)
|
32
|
+
.sort
|
33
|
+
.each_with_index
|
34
|
+
.map{|string, i| "#{i+1}. #{string}" } +
|
35
|
+
["For more info, check out https://www.relishapp.com/gsmendoza/verified-double."]
|
36
|
+
puts output.join("\n\n")
|
30
37
|
end
|
31
38
|
self
|
32
39
|
end
|
@@ -9,7 +9,7 @@ module VerifiedDouble
|
|
9
9
|
def args
|
10
10
|
results = string.scan(/\((.*)\)/)[0]
|
11
11
|
if results
|
12
|
-
results[0].split(',').map{|arg|
|
12
|
+
results[0].split(',').map{|arg| MethodSignature::Value.from(eval(arg)) }
|
13
13
|
else
|
14
14
|
[]
|
15
15
|
end
|
@@ -40,7 +40,7 @@ module VerifiedDouble
|
|
40
40
|
results = string.scan(/=>(.*)/)[0]
|
41
41
|
if results
|
42
42
|
results[0].split(',').map{|return_value|
|
43
|
-
|
43
|
+
MethodSignature::Value.from(eval(return_value)) }
|
44
44
|
else
|
45
45
|
[]
|
46
46
|
end
|
@@ -21,11 +21,15 @@ module VerifiedDouble
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def and_return(return_value)
|
24
|
-
self.method_signatures.last.return_values = [
|
24
|
+
self.method_signatures.last.return_values = [MethodSignature::Value.from(return_value)]
|
25
25
|
@double_call.and_return(return_value)
|
26
26
|
self
|
27
27
|
end
|
28
28
|
|
29
|
+
def class
|
30
|
+
class_double? ? Class : doubled_class
|
31
|
+
end
|
32
|
+
|
29
33
|
def class_double?
|
30
34
|
! double.is_a?(RSpec::Mocks::Mock)
|
31
35
|
end
|
@@ -34,6 +38,10 @@ module VerifiedDouble
|
|
34
38
|
__getobj__
|
35
39
|
end
|
36
40
|
|
41
|
+
def doubled_class
|
42
|
+
class_name.constantize
|
43
|
+
end
|
44
|
+
|
37
45
|
def inspect
|
38
46
|
to_s
|
39
47
|
end
|
@@ -64,7 +72,7 @@ module VerifiedDouble
|
|
64
72
|
|
65
73
|
def with(*args)
|
66
74
|
self.method_signatures.last.args =
|
67
|
-
args.map{|arg|
|
75
|
+
args.map{|arg| MethodSignature::Value.from(arg) }
|
68
76
|
@double_call.with(*args)
|
69
77
|
self
|
70
78
|
end
|
@@ -72,10 +80,11 @@ module VerifiedDouble
|
|
72
80
|
private
|
73
81
|
|
74
82
|
def add_method_signature(method)
|
75
|
-
method_signature =
|
83
|
+
method_signature = RecordedMethodSignature.new(
|
76
84
|
class_name: class_name,
|
77
85
|
method_operator: method_operator,
|
78
|
-
method: method.to_s
|
86
|
+
method: method.to_s,
|
87
|
+
stack_frame: StackFrame.new(caller(2)[0]))
|
79
88
|
|
80
89
|
self.method_signatures << method_signature
|
81
90
|
end
|