verified_double 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -1
- data/CHANGELOG.markdown +12 -0
- data/features/CHANGELOG.markdown +12 -0
- data/features/customizing_arguments_and_return_values.feature +105 -0
- data/lib/verified_double.rb +3 -0
- data/lib/verified_double/boolean.rb +4 -0
- data/lib/verified_double/get_registered_signatures.rb +1 -1
- data/lib/verified_double/get_unverified_signatures.rb +3 -1
- data/lib/verified_double/get_verified_signatures.rb +1 -0
- data/lib/verified_double/method_signature.rb +45 -6
- data/lib/verified_double/method_signature_value.rb +29 -0
- data/lib/verified_double/output_unverified_signatures.rb +1 -1
- data/lib/verified_double/parse_method_signature.rb +49 -0
- data/lib/verified_double/recording_double.rb +8 -4
- data/lib/verified_double/version.rb +1 -1
- data/spec/unit_helper.rb +8 -0
- data/spec/verified_double/get_registered_signatures_spec.rb +4 -4
- data/spec/verified_double/get_unverified_signatures_spec.rb +28 -4
- data/spec/verified_double/get_verified_signatures_spec.rb +32 -2
- data/spec/verified_double/method_signature_spec.rb +119 -16
- data/spec/verified_double/method_signature_value_spec.rb +82 -0
- data/spec/verified_double/output_unverified_signatures_spec.rb +23 -3
- data/spec/verified_double/parse_method_signature_spec.rb +95 -0
- data/spec/verified_double/recording_double_spec.rb +7 -3
- data/verified_double.gemspec +1 -1
- metadata +18 -6
data/.travis.yml
CHANGED
data/CHANGELOG.markdown
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
0.0.2 - 2013-06-22
|
2
|
+
------------------
|
3
|
+
|
4
|
+
* Passes #1: Customizing arguments and return values.
|
5
|
+
* Set rspec-fire to 1.1 for the meantime.
|
6
|
+
|
7
|
+
0.0.1 - 2013-06-02
|
8
|
+
------------------
|
9
|
+
|
10
|
+
* Initial release. Passes "I want to be informed if the mocks I use are verified by contract tests" feature.
|
11
|
+
|
12
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
0.0.2 - 2013-06-22
|
2
|
+
------------------
|
3
|
+
|
4
|
+
* Passes #1: Customizing arguments and return values.
|
5
|
+
* Set rspec-fire to 1.1 for the meantime.
|
6
|
+
|
7
|
+
0.0.1 - 2013-06-02
|
8
|
+
------------------
|
9
|
+
|
10
|
+
* Initial release. Passes "I want to be informed if the mocks I use are verified by contract tests" feature.
|
11
|
+
|
12
|
+
|
@@ -0,0 +1,105 @@
|
|
1
|
+
Feature: Customizing arguments and return values
|
2
|
+
As a developer
|
3
|
+
I want the ability to make contract arguments and return values more or less specific
|
4
|
+
|
5
|
+
Background:
|
6
|
+
Given the following classes:
|
7
|
+
"""
|
8
|
+
class ObjectUnderTest
|
9
|
+
def do_something(collaborator, input)
|
10
|
+
collaborator.some_method(input)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Collaborator
|
15
|
+
def some_method(input)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
"""
|
19
|
+
|
20
|
+
And the test suite has an after(:suite) callback asking VerifiedDouble to report unverified doubles:
|
21
|
+
"""
|
22
|
+
require 'verified_double'
|
23
|
+
require 'main'
|
24
|
+
|
25
|
+
RSpec.configure do |config|
|
26
|
+
config.after :suite do
|
27
|
+
VerifiedDouble::ReportUnverifiedSignatures.new(VerifiedDouble.registry, self).execute
|
28
|
+
end
|
29
|
+
end
|
30
|
+
"""
|
31
|
+
|
32
|
+
And a test that uses VerifiedDouble to mock an object:
|
33
|
+
"""
|
34
|
+
require 'spec_helper'
|
35
|
+
describe ObjectUnderTest do
|
36
|
+
let(:input) { :input }
|
37
|
+
let(:output) { :output }
|
38
|
+
let(:instance_double) { VerifiedDouble.of_instance('Collaborator') }
|
39
|
+
|
40
|
+
it "tests something" do
|
41
|
+
instance_double.should_receive(:some_method).with(input).and_return(output)
|
42
|
+
ObjectUnderTest.new.do_something(instance_double, input)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
"""
|
46
|
+
|
47
|
+
Scenario: More general argument
|
48
|
+
Given the test suite has a contract test for the mock:
|
49
|
+
"""
|
50
|
+
require 'spec_helper'
|
51
|
+
|
52
|
+
describe 'Collaborator' do
|
53
|
+
it "tests something", verifies_contract: 'Collaborator#some_method(Object)=>Symbol' do
|
54
|
+
# do nothing
|
55
|
+
end
|
56
|
+
end
|
57
|
+
"""
|
58
|
+
|
59
|
+
When I run the test suite
|
60
|
+
Then I should not see any output saying the mock is unverified
|
61
|
+
|
62
|
+
Scenario: More specific argument
|
63
|
+
Given the test suite has a contract test for the mock:
|
64
|
+
"""
|
65
|
+
require 'spec_helper'
|
66
|
+
|
67
|
+
describe 'Collaborator' do
|
68
|
+
it "tests something", verifies_contract: 'Collaborator#some_method(:input)=>Symbol' do
|
69
|
+
# do nothing
|
70
|
+
end
|
71
|
+
end
|
72
|
+
"""
|
73
|
+
|
74
|
+
When I run the test suite
|
75
|
+
Then I should not see any output saying the mock is unverified
|
76
|
+
|
77
|
+
Scenario: More general return value
|
78
|
+
Given the test suite has a contract test for the mock:
|
79
|
+
"""
|
80
|
+
require 'spec_helper'
|
81
|
+
|
82
|
+
describe 'Collaborator' do
|
83
|
+
it "tests something", verifies_contract: 'Collaborator#some_method(Symbol)=>Object' do
|
84
|
+
# do nothing
|
85
|
+
end
|
86
|
+
end
|
87
|
+
"""
|
88
|
+
|
89
|
+
When I run the test suite
|
90
|
+
Then I should not see any output saying the mock is unverified
|
91
|
+
|
92
|
+
Scenario: More specific return value
|
93
|
+
Given the test suite has a contract test for the mock:
|
94
|
+
"""
|
95
|
+
require 'spec_helper'
|
96
|
+
|
97
|
+
describe 'Collaborator' do
|
98
|
+
it "tests something", verifies_contract: 'Collaborator#some_method(Symbol)=>:output' do
|
99
|
+
# do nothing
|
100
|
+
end
|
101
|
+
end
|
102
|
+
"""
|
103
|
+
|
104
|
+
When I run the test suite
|
105
|
+
Then I should not see any output saying the mock is unverified
|
data/lib/verified_double.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
require 'rspec/fire'
|
2
2
|
|
3
|
+
require 'verified_double/boolean'
|
3
4
|
require 'verified_double/get_registered_signatures'
|
4
5
|
require 'verified_double/get_unverified_signatures'
|
5
6
|
require 'verified_double/get_verified_signatures'
|
6
7
|
require 'verified_double/method_signature'
|
8
|
+
require 'verified_double/method_signature_value'
|
7
9
|
require 'verified_double/output_unverified_signatures'
|
10
|
+
require 'verified_double/parse_method_signature'
|
8
11
|
require 'verified_double/recording_double'
|
9
12
|
require 'verified_double/report_unverified_signatures'
|
10
13
|
|
@@ -5,7 +5,9 @@ module VerifiedDouble
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def execute
|
8
|
-
@get_registered_signatures.execute
|
8
|
+
@get_registered_signatures.execute.select{|registered_signature|
|
9
|
+
@get_verified_signatures.execute.all?{|verified_signature|
|
10
|
+
! registered_signature.accepts?(verified_signature) } }
|
9
11
|
end
|
10
12
|
end
|
11
13
|
end
|
@@ -1,16 +1,55 @@
|
|
1
1
|
module VerifiedDouble
|
2
2
|
class MethodSignature
|
3
|
-
attr_accessor :args, :method, :
|
3
|
+
attr_accessor :args, :class_name, :method, :method_operator, :return_values
|
4
4
|
|
5
|
-
def initialize(
|
6
|
-
|
5
|
+
def initialize(attributes = {})
|
6
|
+
attributes.each do |name, value|
|
7
|
+
self.send "#{name}=", value
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def accepts?(other)
|
12
|
+
self.class_name == other.class_name &&
|
13
|
+
self.method_operator == other.method_operator &&
|
14
|
+
self.method == other.method &&
|
15
|
+
self.args.size == other.args.size &&
|
16
|
+
(0 ... args.size).all?{|i| self.args[i].accepts?(other.args[i]) } &&
|
17
|
+
self.return_values.size == other.return_values.size &&
|
18
|
+
(0 ... return_values.size).all?{|i| self.return_values[i].accepts?(other.return_values[i]) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def args
|
22
|
+
@args ||= []
|
23
|
+
end
|
24
|
+
|
25
|
+
def eql?(other)
|
26
|
+
to_s.eql?(other.to_s)
|
27
|
+
end
|
28
|
+
|
29
|
+
def hash
|
30
|
+
to_s.hash
|
31
|
+
end
|
32
|
+
|
33
|
+
def recommended_verified_signature
|
34
|
+
self.clone.tap do |result|
|
35
|
+
result.args = result.args.map{|arg| arg.recommended_value }
|
36
|
+
result.return_values = result.return_values.map{|return_value| return_value.recommended_value }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def return_values
|
41
|
+
@return_values ||= []
|
7
42
|
end
|
8
43
|
|
9
44
|
def to_s
|
45
|
+
args_string = args.map(&:value).map{|v| v || 'nil'}.join(', ')
|
46
|
+
return_values_string = return_values.map(&:value).map{|v| v || 'nil'}.join(', ')
|
47
|
+
return_values_string = nil if return_values_string.empty?
|
48
|
+
|
10
49
|
result = [
|
11
|
-
"#{
|
12
|
-
|
13
|
-
result.compact.join("=>")
|
50
|
+
"#{class_name}#{method_operator}#{method}(#{args_string})",
|
51
|
+
return_values_string]
|
52
|
+
result.flatten.compact.join("=>")
|
14
53
|
end
|
15
54
|
end
|
16
55
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module VerifiedDouble
|
2
|
+
class MethodSignatureValue
|
3
|
+
attr_reader :value
|
4
|
+
|
5
|
+
def initialize(value)
|
6
|
+
@value = value
|
7
|
+
end
|
8
|
+
|
9
|
+
def accepts?(other)
|
10
|
+
if self.value.is_a?(Class) || ! other.value.is_a?(Class)
|
11
|
+
self.value == other.value
|
12
|
+
else
|
13
|
+
self.modified_class.ancestors.include?(other.value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def modified_class
|
18
|
+
if value == true or value == false
|
19
|
+
VerifiedDouble::Boolean
|
20
|
+
else
|
21
|
+
value.class
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def recommended_value
|
26
|
+
self.class.new(self.modified_class)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -6,7 +6,7 @@ module VerifiedDouble
|
|
6
6
|
|
7
7
|
def execute
|
8
8
|
if unverified_signatures.any?
|
9
|
-
output = ["The following mocks are not verified:" ] + unverified_signatures
|
9
|
+
output = ["The following mocks are not verified:" ] + unverified_signatures.map(&:recommended_verified_signature)
|
10
10
|
puts output.join("\n")
|
11
11
|
end
|
12
12
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module VerifiedDouble
|
2
|
+
class ParseMethodSignature
|
3
|
+
attr_reader :string
|
4
|
+
|
5
|
+
def initialize(string)
|
6
|
+
@string = string
|
7
|
+
end
|
8
|
+
|
9
|
+
def args
|
10
|
+
results = string.scan(/\((.*)\)/)[0]
|
11
|
+
if results
|
12
|
+
results[0].split(',').map{|arg| MethodSignatureValue.new(eval(arg)) }
|
13
|
+
else
|
14
|
+
[]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def class_name
|
19
|
+
string.scan(/(.*)[\.\#]/)[0][0]
|
20
|
+
end
|
21
|
+
|
22
|
+
def execute
|
23
|
+
MethodSignature.new(
|
24
|
+
class_name: class_name,
|
25
|
+
method_operator: method_operator,
|
26
|
+
method: method,
|
27
|
+
args: args,
|
28
|
+
return_values: return_values)
|
29
|
+
end
|
30
|
+
|
31
|
+
def method
|
32
|
+
string.scan(/[\.\#](.*?)(=>|\(|$)/)[0][0]
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_operator
|
36
|
+
string.scan(/[\.\#]/)[0][0]
|
37
|
+
end
|
38
|
+
|
39
|
+
def return_values
|
40
|
+
results = string.scan(/=>(.*)/)[0]
|
41
|
+
if results
|
42
|
+
results[0].split(',').map{|return_value|
|
43
|
+
MethodSignatureValue.new(eval(return_value)) }
|
44
|
+
else
|
45
|
+
[]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -8,7 +8,7 @@ module VerifiedDouble
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def and_return(return_value)
|
11
|
-
self.method_signatures.last.
|
11
|
+
self.method_signatures.last.return_values = [MethodSignatureValue.new(return_value)]
|
12
12
|
@double_call.and_return(return_value)
|
13
13
|
self
|
14
14
|
end
|
@@ -38,8 +38,11 @@ module VerifiedDouble
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def should_receive(method)
|
41
|
-
method_signature = MethodSignature.new(
|
42
|
-
|
41
|
+
method_signature = MethodSignature.new(
|
42
|
+
class_name: class_name,
|
43
|
+
method_operator: method_operator,
|
44
|
+
method: method.to_s)
|
45
|
+
|
43
46
|
self.method_signatures << method_signature
|
44
47
|
@double_call = super(method)
|
45
48
|
self
|
@@ -50,7 +53,8 @@ module VerifiedDouble
|
|
50
53
|
end
|
51
54
|
|
52
55
|
def with(*args)
|
53
|
-
self.method_signatures.last.args =
|
56
|
+
self.method_signatures.last.args =
|
57
|
+
args.map{|arg| MethodSignatureValue.new(arg) }
|
54
58
|
@double_call.with(*args)
|
55
59
|
self
|
56
60
|
end
|
data/spec/unit_helper.rb
CHANGED
@@ -2,6 +2,14 @@ require 'active_support/core_ext/string'
|
|
2
2
|
require 'pry'
|
3
3
|
require 'rspec/fire'
|
4
4
|
|
5
|
+
require 'verified_double/boolean'
|
6
|
+
|
7
|
+
# Requiring because these are Value objects.
|
8
|
+
# As value objects, we treat them as primitives.
|
9
|
+
# Hence, there should be no need to mock or stub them.
|
10
|
+
require "verified_double/method_signature"
|
11
|
+
require "verified_double/method_signature_value"
|
12
|
+
|
5
13
|
RSpec.configure do |config|
|
6
14
|
config.include(RSpec::Fire)
|
7
15
|
end
|
@@ -3,10 +3,10 @@ require 'verified_double/get_registered_signatures'
|
|
3
3
|
|
4
4
|
describe VerifiedDouble::GetRegisteredSignatures do
|
5
5
|
let(:method_signature_1) {
|
6
|
-
|
6
|
+
VerifiedDouble::MethodSignature.new(class_name: 'Object', method_operator: '#', method: 'to_s') }
|
7
7
|
|
8
8
|
let(:method_signature_2) {
|
9
|
-
|
9
|
+
VerifiedDouble::MethodSignature.new(class_name: 'Object', method_operator: '#', method: 'inspect') }
|
10
10
|
|
11
11
|
let(:recording_double_1) {
|
12
12
|
fire_double('VerifiedDouble::RecordingDouble',
|
@@ -23,7 +23,7 @@ describe VerifiedDouble::GetRegisteredSignatures do
|
|
23
23
|
let(:double_registry){ [recording_double_1, recording_double_2] }
|
24
24
|
|
25
25
|
it "maps and flattens the method signatures of the recording doubles" do
|
26
|
-
expect(subject.execute).to eq([
|
26
|
+
expect(subject.execute).to eq([method_signature_1, method_signature_2])
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -35,7 +35,7 @@ describe VerifiedDouble::GetRegisteredSignatures do
|
|
35
35
|
let(:double_registry){ [recording_double_1, recording_double_2] }
|
36
36
|
|
37
37
|
it "returns distinct method signatures" do
|
38
|
-
expect(subject.execute).to eq([
|
38
|
+
expect(subject.execute).to eq([method_signature_1])
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
@@ -11,16 +11,40 @@ describe VerifiedDouble::GetUnverifiedSignatures do
|
|
11
11
|
subject { described_class.new(get_registered_signatures_service, get_verified_signatures_service) }
|
12
12
|
|
13
13
|
describe "#execute" do
|
14
|
-
|
14
|
+
let(:registered_signature) {
|
15
|
+
VerifiedDouble::MethodSignature.new(
|
16
|
+
class_name: 'Person',
|
17
|
+
method: 'find',
|
18
|
+
method_operator: '.',
|
19
|
+
args: [VerifiedDouble::MethodSignatureValue.new(1)]) }
|
20
|
+
|
21
|
+
let(:registered_signature_without_match) {
|
22
|
+
VerifiedDouble::MethodSignature.new(
|
23
|
+
class_name: 'Person',
|
24
|
+
method: 'save!',
|
25
|
+
method_operator: '#') }
|
26
|
+
|
27
|
+
let(:verified_signature) {
|
28
|
+
VerifiedDouble::MethodSignature.new(
|
29
|
+
class_name: 'Person',
|
30
|
+
method: 'find',
|
31
|
+
method_operator: '.',
|
32
|
+
args: [VerifiedDouble::MethodSignatureValue.new(Object)]) }
|
33
|
+
|
34
|
+
it "retains registered signatures that cannot accept any of the verified_signatures" do
|
35
|
+
expect(registered_signature.accepts?(verified_signature)).to be_true
|
36
|
+
expect(registered_signature_without_match.accepts?(verified_signature)).to be_false
|
37
|
+
|
15
38
|
get_registered_signatures_service
|
16
39
|
.should_receive(:execute)
|
17
|
-
.and_return([
|
40
|
+
.and_return([registered_signature, registered_signature_without_match])
|
18
41
|
|
19
42
|
get_verified_signatures_service
|
20
43
|
.should_receive(:execute)
|
21
|
-
.
|
44
|
+
.at_least(:once)
|
45
|
+
.and_return([verified_signature])
|
22
46
|
|
23
|
-
expect(subject.execute).to eq([
|
47
|
+
expect(subject.execute).to eq([registered_signature_without_match])
|
24
48
|
end
|
25
49
|
end
|
26
50
|
end
|
@@ -2,7 +2,16 @@ require 'unit_helper'
|
|
2
2
|
require 'verified_double/get_verified_signatures'
|
3
3
|
|
4
4
|
describe VerifiedDouble::GetVerifiedSignatures do
|
5
|
+
let(:method_signature) { VerifiedDouble::MethodSignature.new }
|
6
|
+
|
5
7
|
let(:nested_example_group){ double(:nested_example_group) }
|
8
|
+
|
9
|
+
let(:parse_method_signature_service) {
|
10
|
+
fire_double('VerifiedDouble::ParseMethodSignature') }
|
11
|
+
|
12
|
+
let(:parse_method_signature_service_class) {
|
13
|
+
fire_class_double('VerifiedDouble::ParseMethodSignature').as_replaced_constant }
|
14
|
+
|
6
15
|
subject { described_class.new(nested_example_group) }
|
7
16
|
|
8
17
|
describe "#execute" do
|
@@ -18,14 +27,35 @@ describe VerifiedDouble::GetVerifiedSignatures do
|
|
18
27
|
nested_example_group
|
19
28
|
.stub_chain(:class, :descendant_filtered_examples)
|
20
29
|
.and_return([example_with_verified_contract_tag, example_without_verified_contract_tag])
|
21
|
-
|
30
|
+
|
31
|
+
parse_method_signature_service_class
|
32
|
+
.should_receive(:new)
|
33
|
+
.with(example_with_verified_contract_tag.metadata[:verifies_contract])
|
34
|
+
.and_return(parse_method_signature_service)
|
35
|
+
|
36
|
+
parse_method_signature_service
|
37
|
+
.should_receive(:execute)
|
38
|
+
.and_return(method_signature)
|
39
|
+
|
40
|
+
expect(subject.execute).to eq([method_signature])
|
22
41
|
end
|
23
42
|
|
24
43
|
it "returns unique signatures" do
|
25
44
|
nested_example_group
|
26
45
|
.stub_chain(:class, :descendant_filtered_examples)
|
27
46
|
.and_return([example_with_verified_contract_tag, example_with_verified_contract_tag])
|
28
|
-
|
47
|
+
|
48
|
+
parse_method_signature_service_class
|
49
|
+
.should_receive(:new)
|
50
|
+
.with(example_with_verified_contract_tag.metadata[:verifies_contract])
|
51
|
+
.at_least(:once)
|
52
|
+
.and_return(parse_method_signature_service)
|
53
|
+
|
54
|
+
parse_method_signature_service
|
55
|
+
.should_receive(:execute)
|
56
|
+
.and_return(method_signature)
|
57
|
+
|
58
|
+
expect(subject.execute).to eq([method_signature])
|
29
59
|
end
|
30
60
|
end
|
31
61
|
end
|
@@ -2,49 +2,152 @@ require 'unit_helper'
|
|
2
2
|
require 'verified_double/method_signature'
|
3
3
|
|
4
4
|
describe VerifiedDouble::MethodSignature do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
class Dummy
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:attributes){
|
9
|
+
{ class_name: 'Dummy',
|
10
|
+
method_operator: '#' } }
|
11
|
+
|
12
|
+
describe "#initialize" do
|
13
|
+
it { expect(subject.return_values).to be_empty }
|
14
|
+
it { expect(subject.args).to be_empty }
|
15
|
+
end
|
10
16
|
|
17
|
+
describe "#to_s" do
|
11
18
|
subject {
|
12
|
-
described_class.new(
|
19
|
+
described_class.new(attributes).tap {|method_signature|
|
13
20
|
method_signature.method = 'do_something' } }
|
14
21
|
|
15
22
|
context "when there are args" do
|
16
|
-
it "includes the
|
17
|
-
subject.args = [1, {}]
|
18
|
-
expect(subject.to_s).to eq("Dummy#do_something(
|
23
|
+
it "includes the arg values in the result" do
|
24
|
+
subject.args = [VerifiedDouble::MethodSignatureValue.new(1), VerifiedDouble::MethodSignatureValue.new({})]
|
25
|
+
expect(subject.to_s).to eq("Dummy#do_something(1, {})")
|
19
26
|
end
|
20
27
|
end
|
21
28
|
|
22
29
|
context "when there are no args" do
|
23
30
|
it "displays an empty parenthesis for the args of the result" do
|
24
|
-
expect(subject.
|
31
|
+
expect(subject.args).to be_empty
|
25
32
|
expect(subject.to_s).to eq("Dummy#do_something()")
|
26
33
|
end
|
27
34
|
end
|
28
35
|
|
29
36
|
context "when there is a nil arg" do
|
30
|
-
it "displays
|
31
|
-
subject.args = [nil]
|
32
|
-
expect(subject.to_s).to eq("Dummy#do_something(
|
37
|
+
it "displays nil for the arg of the result" do
|
38
|
+
subject.args = [VerifiedDouble::MethodSignatureValue.new(nil)]
|
39
|
+
expect(subject.to_s).to eq("Dummy#do_something(nil)")
|
33
40
|
end
|
34
41
|
end
|
35
42
|
|
36
43
|
context "where there is a return value" do
|
37
44
|
it "displays the return value" do
|
38
|
-
subject.
|
39
|
-
expect(subject.to_s).to eq("Dummy#do_something()=>
|
45
|
+
subject.return_values = [VerifiedDouble::MethodSignatureValue.new(true)]
|
46
|
+
expect(subject.to_s).to eq("Dummy#do_something()=>true")
|
40
47
|
end
|
41
48
|
end
|
42
49
|
|
43
50
|
context "where there is no return value" do
|
44
51
|
it "does not include the hash rocket in the result" do
|
45
|
-
expect(subject.
|
52
|
+
expect(subject.return_values).to be_empty
|
46
53
|
expect(subject.to_s).to eq("Dummy#do_something()")
|
47
54
|
end
|
48
55
|
end
|
49
56
|
end
|
57
|
+
|
58
|
+
context "multiple method signatures with the same #to_s" do
|
59
|
+
let(:method_signature){
|
60
|
+
described_class.new(attributes).tap {|method_signature|
|
61
|
+
method_signature.method = 'do_something' } }
|
62
|
+
|
63
|
+
let(:method_signature_with_same_to_s){
|
64
|
+
described_class.new(attributes).tap {|method_signature|
|
65
|
+
method_signature.method = 'do_something' } }
|
66
|
+
|
67
|
+
it { expect(method_signature.to_s).to eq(method_signature_with_same_to_s.to_s) }
|
68
|
+
it { expect(method_signature.hash).to eq(method_signature_with_same_to_s.hash) }
|
69
|
+
it { expect(method_signature.eql?(method_signature_with_same_to_s)).to be_true }
|
70
|
+
it { expect([method_signature, method_signature_with_same_to_s].uniq == [method_signature]).to be_true }
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#accepts?(other)" do
|
74
|
+
let(:method_signature){
|
75
|
+
described_class.new(
|
76
|
+
class_name: 'Dummy',
|
77
|
+
method_operator: '.',
|
78
|
+
method: 'find',
|
79
|
+
args: [VerifiedDouble::MethodSignatureValue.new(1)],
|
80
|
+
return_values: [VerifiedDouble::MethodSignatureValue.new(Dummy.new)]) }
|
81
|
+
|
82
|
+
subject { method_signature.accepts?(other) }
|
83
|
+
|
84
|
+
context "where self has same attributes as other" do
|
85
|
+
let(:other){ method_signature.clone }
|
86
|
+
it { expect(subject).to be_true }
|
87
|
+
end
|
88
|
+
|
89
|
+
context "where self and other have different class names" do
|
90
|
+
let(:other){ method_signature.clone.tap{|ms| ms.class_name = 'Object' } }
|
91
|
+
it { expect(subject).to be_false }
|
92
|
+
end
|
93
|
+
|
94
|
+
context "where self and other have different method operators" do
|
95
|
+
let(:other){ method_signature.clone.tap{|ms| ms.method_operator = '#' } }
|
96
|
+
it { expect(subject).to be_false }
|
97
|
+
end
|
98
|
+
|
99
|
+
context "where self and other have different methods" do
|
100
|
+
let(:other){ method_signature.clone.tap{|ms| ms.method = 'destroy' } }
|
101
|
+
it { expect(subject).to be_false }
|
102
|
+
end
|
103
|
+
|
104
|
+
context "where self and other have different number of args" do
|
105
|
+
let(:other){
|
106
|
+
method_signature.clone.tap{|ms|
|
107
|
+
ms.args = [VerifiedDouble::MethodSignatureValue.new(1), VerifiedDouble::MethodSignatureValue.new(2)] } }
|
108
|
+
|
109
|
+
it { expect(subject).to be_false }
|
110
|
+
end
|
111
|
+
|
112
|
+
context "where not all of self's args accept the args of other" do
|
113
|
+
let(:other){
|
114
|
+
method_signature.clone.tap{|ms|
|
115
|
+
ms.args = [VerifiedDouble::MethodSignatureValue.new(2)] } }
|
116
|
+
|
117
|
+
it { expect(subject).to be_false }
|
118
|
+
end
|
119
|
+
|
120
|
+
context "where self and other have different number of return values" do
|
121
|
+
let(:other){
|
122
|
+
method_signature.clone.tap{|ms|
|
123
|
+
ms.return_values = [VerifiedDouble::MethodSignatureValue.new(1), VerifiedDouble::MethodSignatureValue.new(2)] } }
|
124
|
+
|
125
|
+
it { expect(subject).to be_false }
|
126
|
+
end
|
127
|
+
|
128
|
+
context "where not all of self's return values accept the return values of other" do
|
129
|
+
let(:other){
|
130
|
+
method_signature.clone.tap{|ms|
|
131
|
+
ms.return_values = [VerifiedDouble::MethodSignatureValue.new(Symbol)] } }
|
132
|
+
|
133
|
+
it { expect(subject).to be_false }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe "#recommended_verified_signature" do
|
138
|
+
let(:method_signature){
|
139
|
+
described_class.new(
|
140
|
+
class_name: 'Dummy',
|
141
|
+
method_operator: '.',
|
142
|
+
method: 'find',
|
143
|
+
args: [VerifiedDouble::MethodSignatureValue.new(1)],
|
144
|
+
return_values: [VerifiedDouble::MethodSignatureValue.new(Dummy.new)]) }
|
145
|
+
|
146
|
+
subject { method_signature.recommended_verified_signature }
|
147
|
+
|
148
|
+
it "is a method signature that is recommended for the user to verify" do
|
149
|
+
expect(subject.args[0].value).to eq(method_signature.args[0].recommended_value.value)
|
150
|
+
expect(subject.return_values[0].value).to eq(method_signature.return_values[0].recommended_value.value)
|
151
|
+
end
|
152
|
+
end
|
50
153
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'unit_helper'
|
2
|
+
require 'verified_double/method_signature_value'
|
3
|
+
|
4
|
+
describe VerifiedDouble::MethodSignatureValue do
|
5
|
+
let(:value){ :some_value }
|
6
|
+
|
7
|
+
describe "#initialize" do
|
8
|
+
it "requires a value from a method signature" do
|
9
|
+
described_class.new(value)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#accepts?(other)" do
|
14
|
+
subject { this.accepts?(other) }
|
15
|
+
|
16
|
+
context "where self's value is an actual class and other's value matches it" do
|
17
|
+
let(:this){ described_class.new(Fixnum) }
|
18
|
+
let(:other){ described_class.new(Fixnum) }
|
19
|
+
it { expect(subject).to be_true }
|
20
|
+
end
|
21
|
+
|
22
|
+
context "where self's value is an actual class and other's value does not match it" do
|
23
|
+
let(:this){ described_class.new(Fixnum) }
|
24
|
+
let(:other){ described_class.new(Object) }
|
25
|
+
it { expect(subject).to be_false }
|
26
|
+
end
|
27
|
+
|
28
|
+
context "where the other value is an instance and self's value matches it" do
|
29
|
+
let(:this){ described_class.new(1) }
|
30
|
+
let(:other){ described_class.new(1) }
|
31
|
+
it { expect(subject).to be_true }
|
32
|
+
end
|
33
|
+
|
34
|
+
context "where the other value is an instance and self's value does not it" do
|
35
|
+
let(:this){ described_class.new(2) }
|
36
|
+
let(:other){ described_class.new(1) }
|
37
|
+
it { expect(subject).to be_false }
|
38
|
+
end
|
39
|
+
|
40
|
+
context "where self is an instance and the other's class is an ancestor of self's modified class" do
|
41
|
+
let(:this){ described_class.new(1) }
|
42
|
+
let(:other){ described_class.new(Object) }
|
43
|
+
it { expect(subject).to be_true }
|
44
|
+
end
|
45
|
+
|
46
|
+
context "where self is an instance and the other's class is not an ancestor of self's modified class" do
|
47
|
+
let(:this){ described_class.new(1) }
|
48
|
+
let(:other){ described_class.new(Float) }
|
49
|
+
it { expect(subject).to be_false }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#modified_class" do
|
54
|
+
subject { method_signature_value.modified_class }
|
55
|
+
|
56
|
+
context "where the value is true" do
|
57
|
+
let(:method_signature_value) { described_class.new(true) }
|
58
|
+
it { expect(subject).to eq(VerifiedDouble::Boolean) }
|
59
|
+
end
|
60
|
+
|
61
|
+
context "where the value is false" do
|
62
|
+
let(:method_signature_value) { described_class.new(false) }
|
63
|
+
it { expect(subject).to eq(VerifiedDouble::Boolean) }
|
64
|
+
end
|
65
|
+
|
66
|
+
context "where the value is not true or false" do
|
67
|
+
let(:method_signature_value) { described_class.new(1) }
|
68
|
+
it "is the class of the value" do
|
69
|
+
expect(subject).to eq(Fixnum)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "#recommended_value" do
|
75
|
+
subject { described_class.new(value) }
|
76
|
+
|
77
|
+
it "is a version of self that will be recommended to users to verify" do
|
78
|
+
expect(subject.recommended_value.value).to eq(subject.modified_class)
|
79
|
+
expect(subject.recommended_value.value).to_not eq(subject)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -2,10 +2,25 @@ require 'unit_helper'
|
|
2
2
|
require 'verified_double/output_unverified_signatures'
|
3
3
|
|
4
4
|
describe VerifiedDouble::OutputUnverifiedSignatures do
|
5
|
+
class Dummy
|
6
|
+
end
|
7
|
+
|
5
8
|
let(:get_unverified_signatures_service){
|
6
9
|
fire_double('VerifiedDouble::GetUnverifiedSignatures') }
|
7
10
|
|
8
|
-
let(:unverified_signatures){
|
11
|
+
let(:unverified_signatures){ [
|
12
|
+
VerifiedDouble::MethodSignature.new(
|
13
|
+
class_name: 'Dummy',
|
14
|
+
method_operator: '.',
|
15
|
+
method: 'find',
|
16
|
+
args: [VerifiedDouble::MethodSignatureValue.new(1)],
|
17
|
+
return_values: [VerifiedDouble::MethodSignatureValue.new(Dummy.new)]),
|
18
|
+
VerifiedDouble::MethodSignature.new(
|
19
|
+
class_name: 'Dummy',
|
20
|
+
method_operator: '.',
|
21
|
+
method: 'where',
|
22
|
+
args: [VerifiedDouble::MethodSignatureValue.new(id: 1)],
|
23
|
+
return_values: [VerifiedDouble::MethodSignatureValue.new(Dummy.new)]) ] }
|
9
24
|
|
10
25
|
subject { described_class.new(get_unverified_signatures_service) }
|
11
26
|
|
@@ -20,10 +35,15 @@ describe VerifiedDouble::OutputUnverifiedSignatures do
|
|
20
35
|
end
|
21
36
|
|
22
37
|
context "where there are unverified_signatures" do
|
23
|
-
it "should output the unverified_signatures" do
|
38
|
+
it "should output the recommended versions of the unverified_signatures" do
|
24
39
|
get_unverified_signatures_service.should_receive(:execute).and_return(unverified_signatures)
|
25
|
-
subject.should_receive(:puts).with("The following mocks are not verified:\nUNVERIFIED_SIGNATURES_1\nUNVERIFIED_SIGNATURES_2")
|
26
40
|
|
41
|
+
lines = [
|
42
|
+
"The following mocks are not verified:",
|
43
|
+
unverified_signatures[0].recommended_verified_signature,
|
44
|
+
unverified_signatures[1].recommended_verified_signature ]
|
45
|
+
|
46
|
+
subject.should_receive(:puts).with(lines.join("\n"))
|
27
47
|
subject.execute
|
28
48
|
end
|
29
49
|
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'unit_helper'
|
2
|
+
require 'verified_double/parse_method_signature'
|
3
|
+
|
4
|
+
describe VerifiedDouble::ParseMethodSignature do
|
5
|
+
subject { described_class.new(string) }
|
6
|
+
|
7
|
+
describe "#execute" do
|
8
|
+
let(:string){ "Class#method(:arg_1, :arg_2)=>:return_value" }
|
9
|
+
|
10
|
+
subject { described_class.new(string).execute }
|
11
|
+
|
12
|
+
it "returns a method signature from the signature string" do
|
13
|
+
expect(subject).to be_a(VerifiedDouble::MethodSignature)
|
14
|
+
expect(subject.class_name).to eq(subject.class_name)
|
15
|
+
expect(subject.method_operator).to eq(subject.method_operator)
|
16
|
+
expect(subject.method).to eq(subject.method)
|
17
|
+
expect(subject.args).to eq(subject.args)
|
18
|
+
expect(subject.return_values).to eq(subject.return_values)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#method_operator" do
|
23
|
+
context "for Class.method" do
|
24
|
+
let(:string){ "Class.method" }
|
25
|
+
it { expect(subject.method_operator).to eq('.') }
|
26
|
+
end
|
27
|
+
|
28
|
+
context "for Class#method" do
|
29
|
+
let(:string){ "Class#method" }
|
30
|
+
it { expect(subject.method_operator).to eq('#') }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#class_name" do
|
35
|
+
context "for Class.method" do
|
36
|
+
let(:string){ "Class.method" }
|
37
|
+
it { expect(subject.class_name).to eq('Class') }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#method" do
|
42
|
+
context "for Class.method" do
|
43
|
+
let(:string){ "Class.method_1!?" }
|
44
|
+
it { expect(subject.method).to eq('method_1!?') }
|
45
|
+
end
|
46
|
+
|
47
|
+
context "for Class.method(:arg_1, :arg_2)" do
|
48
|
+
let(:string){ "Class.method_1!?(:arg_1, :arg_2)" }
|
49
|
+
it { expect(subject.method).to eq('method_1!?') }
|
50
|
+
end
|
51
|
+
|
52
|
+
context "for Class.method=>return_value" do
|
53
|
+
let(:string){ "Class.method_1!?=>return_value" }
|
54
|
+
it { expect(subject.method).to eq('method_1!?') }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
describe "#args" do
|
60
|
+
context "for Class.method(:arg_1, :arg_2)" do
|
61
|
+
let(:string){ "Class.method(:arg_1, :arg_2)" }
|
62
|
+
|
63
|
+
it "builds method signature values from the evals of the args" do
|
64
|
+
expect(subject.args.map(&:value)).to eq([:arg_1, :arg_2])
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "for Class.method" do
|
69
|
+
let(:string){ "Class.method" }
|
70
|
+
it { expect(subject.args).to eq([]) }
|
71
|
+
end
|
72
|
+
|
73
|
+
context "for Class.method=>:return_value" do
|
74
|
+
let(:string){ "Class.method=>:return_value" }
|
75
|
+
it { expect(subject.args).to eq([]) }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#return_values" do
|
80
|
+
context "for Class.method=>:return_value" do
|
81
|
+
let(:string){ "Class.method=>:return_value" }
|
82
|
+
it { expect(subject.return_values.map(&:value)).to eq([:return_value]) }
|
83
|
+
end
|
84
|
+
|
85
|
+
context "for Class.method" do
|
86
|
+
let(:string){ "Class.method" }
|
87
|
+
it { expect(subject.return_values).to be_empty }
|
88
|
+
end
|
89
|
+
|
90
|
+
context "for Class.method(:arg_1, :arg_2)" do
|
91
|
+
let(:string){ "Class.method(:arg_1, :arg_2)" }
|
92
|
+
it { expect(subject.return_values).to be_empty }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -104,7 +104,7 @@ describe VerifiedDouble::RecordingDouble do
|
|
104
104
|
subject.should_receive(:to_s)
|
105
105
|
subject.should_receive(:inspect)
|
106
106
|
|
107
|
-
expect(subject.method_signatures.map(&:method)).to eq([
|
107
|
+
expect(subject.method_signatures.map(&:method)).to eq(['to_s', 'inspect'])
|
108
108
|
|
109
109
|
subject.to_s
|
110
110
|
subject.inspect
|
@@ -115,7 +115,8 @@ describe VerifiedDouble::RecordingDouble do
|
|
115
115
|
it "sets the args of the last method signature" do
|
116
116
|
subject.should_receive(:to_s).with(:arg_1, :arg_2)
|
117
117
|
|
118
|
-
expect(subject.method_signatures[0].args).to
|
118
|
+
expect(subject.method_signatures[0].args).to be_all{|arg| arg.is_a?(VerifiedDouble::MethodSignatureValue) }
|
119
|
+
expect(subject.method_signatures[0].args.map(&:value)).to eq([:arg_1, :arg_2])
|
119
120
|
|
120
121
|
subject.to_s(:arg_1, :arg_2)
|
121
122
|
end
|
@@ -125,7 +126,10 @@ describe VerifiedDouble::RecordingDouble do
|
|
125
126
|
it "sets the return value of the last method signature" do
|
126
127
|
subject.should_receive(:to_s).with(:arg_1, :arg_2).and_return(:return_value)
|
127
128
|
|
128
|
-
|
129
|
+
return_values = subject.method_signatures[0].return_values
|
130
|
+
expect(return_values).to have(1).return_value
|
131
|
+
expect(return_values.first).to be_a(VerifiedDouble::MethodSignatureValue)
|
132
|
+
expect(return_values.first.value).to eq(:return_value)
|
129
133
|
|
130
134
|
subject.to_s(:arg_1, :arg_2)
|
131
135
|
end
|
data/verified_double.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |gem|
|
|
18
18
|
gem.require_paths = ["lib"]
|
19
19
|
|
20
20
|
gem.add_runtime_dependency "rspec"
|
21
|
-
gem.add_runtime_dependency "rspec-fire"
|
21
|
+
gem.add_runtime_dependency "rspec-fire", '~> 1.1.3'
|
22
22
|
|
23
23
|
gem.add_development_dependency "aruba"
|
24
24
|
gem.add_development_dependency "cucumber"
|
metadata
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
name: verified_double
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- George Mendoza
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-06-
|
12
|
+
date: 2013-06-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
type: :runtime
|
@@ -32,16 +32,16 @@ dependencies:
|
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
33
33
|
none: false
|
34
34
|
requirements:
|
35
|
-
- -
|
35
|
+
- - ~>
|
36
36
|
- !ruby/object:Gem::Version
|
37
|
-
version:
|
37
|
+
version: 1.1.3
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
|
-
- -
|
42
|
+
- - ~>
|
43
43
|
- !ruby/object:Gem::Version
|
44
|
-
version:
|
44
|
+
version: 1.1.3
|
45
45
|
name: rspec-fire
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
47
|
type: :development
|
@@ -116,20 +116,26 @@ extra_rdoc_files: []
|
|
116
116
|
files:
|
117
117
|
- .gitignore
|
118
118
|
- .travis.yml
|
119
|
+
- CHANGELOG.markdown
|
119
120
|
- Gemfile
|
120
121
|
- LICENSE.txt
|
121
122
|
- README.md
|
122
123
|
- Rakefile
|
124
|
+
- features/CHANGELOG.markdown
|
125
|
+
- features/customizing_arguments_and_return_values.feature
|
123
126
|
- features/readme.md
|
124
127
|
- features/step_definitions/verified_double_steps.rb
|
125
128
|
- features/support/requires.rb
|
126
129
|
- features/verified_double.feature
|
127
130
|
- lib/verified_double.rb
|
131
|
+
- lib/verified_double/boolean.rb
|
128
132
|
- lib/verified_double/get_registered_signatures.rb
|
129
133
|
- lib/verified_double/get_unverified_signatures.rb
|
130
134
|
- lib/verified_double/get_verified_signatures.rb
|
131
135
|
- lib/verified_double/method_signature.rb
|
136
|
+
- lib/verified_double/method_signature_value.rb
|
132
137
|
- lib/verified_double/output_unverified_signatures.rb
|
138
|
+
- lib/verified_double/parse_method_signature.rb
|
133
139
|
- lib/verified_double/recording_double.rb
|
134
140
|
- lib/verified_double/report_unverified_signatures.rb
|
135
141
|
- lib/verified_double/verify_doubles_service.rb
|
@@ -140,7 +146,9 @@ files:
|
|
140
146
|
- spec/verified_double/get_unverified_signatures_spec.rb
|
141
147
|
- spec/verified_double/get_verified_signatures_spec.rb
|
142
148
|
- spec/verified_double/method_signature_spec.rb
|
149
|
+
- spec/verified_double/method_signature_value_spec.rb
|
143
150
|
- spec/verified_double/output_unverified_signatures_spec.rb
|
151
|
+
- spec/verified_double/parse_method_signature_spec.rb
|
144
152
|
- spec/verified_double/recording_double_spec.rb
|
145
153
|
- spec/verified_double/report_unverified_signatures_spec.rb
|
146
154
|
- spec/verified_double/verify_doubles_service_spec.rb
|
@@ -172,6 +180,8 @@ specification_version: 3
|
|
172
180
|
summary: VerifiedDouble would record any mock made in the test suite. It would then
|
173
181
|
verify if the mock is valid by checking if there is a test against it.
|
174
182
|
test_files:
|
183
|
+
- features/CHANGELOG.markdown
|
184
|
+
- features/customizing_arguments_and_return_values.feature
|
175
185
|
- features/readme.md
|
176
186
|
- features/step_definitions/verified_double_steps.rb
|
177
187
|
- features/support/requires.rb
|
@@ -182,7 +192,9 @@ test_files:
|
|
182
192
|
- spec/verified_double/get_unverified_signatures_spec.rb
|
183
193
|
- spec/verified_double/get_verified_signatures_spec.rb
|
184
194
|
- spec/verified_double/method_signature_spec.rb
|
195
|
+
- spec/verified_double/method_signature_value_spec.rb
|
185
196
|
- spec/verified_double/output_unverified_signatures_spec.rb
|
197
|
+
- spec/verified_double/parse_method_signature_spec.rb
|
186
198
|
- spec/verified_double/recording_double_spec.rb
|
187
199
|
- spec/verified_double/report_unverified_signatures_spec.rb
|
188
200
|
- spec/verified_double/verify_doubles_service_spec.rb
|