duck_testing 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 +35 -0
- data/.rspec +3 -0
- data/.rubocop.yml +15 -0
- data/.travis.yml +7 -0
- data/.yardopts +4 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +7 -0
- data/Guardfile +19 -0
- data/LICENSE +22 -0
- data/README.md +75 -0
- data/Rakefile +8 -0
- data/duck_testing.gemspec +28 -0
- data/lib/duck_testing.rb +19 -0
- data/lib/duck_testing/errors.rb +4 -0
- data/lib/duck_testing/method_call_data.rb +19 -0
- data/lib/duck_testing/reporter/base.rb +17 -0
- data/lib/duck_testing/reporter/raise_error.rb +30 -0
- data/lib/duck_testing/tester.rb +50 -0
- data/lib/duck_testing/type/base.rb +13 -0
- data/lib/duck_testing/type/class_instance.rb +24 -0
- data/lib/duck_testing/type/constant.rb +26 -0
- data/lib/duck_testing/type/duck_type.rb +24 -0
- data/lib/duck_testing/type/hash.rb +60 -0
- data/lib/duck_testing/type/order_dependent_array.rb +27 -0
- data/lib/duck_testing/type/order_independent_array.rb +27 -0
- data/lib/duck_testing/version.rb +3 -0
- data/lib/duck_testing/violation.rb +41 -0
- data/lib/duck_testing/yard.rb +51 -0
- data/lib/duck_testing/yard/builder.rb +70 -0
- data/lib/duck_testing/yard/class_object.rb +20 -0
- data/lib/duck_testing/yard/code_object.rb +22 -0
- data/lib/duck_testing/yard/method_object.rb +87 -0
- data/lib/duck_testing/yard/method_parameter.rb +49 -0
- data/lib/duck_testing/yard/parser.rb +30 -0
- data/sample/.gitignore +35 -0
- data/sample/.rspec +3 -0
- data/sample/Gemfile +5 -0
- data/sample/lib/concern.rb +9 -0
- data/sample/lib/sample.rb +14 -0
- data/sample/spec/sample_spec.rb +33 -0
- data/sample/spec/spec_helper.rb +4 -0
- data/spec/.rubocop.yml +8 -0
- data/spec/class_type_integration_spec.rb +98 -0
- data/spec/constant_type_integration_spec.rb +90 -0
- data/spec/duck_testing/method_call_data_spec.rb +24 -0
- data/spec/duck_testing/reporter/raise_error_spec.rb +35 -0
- data/spec/duck_testing/tester_spec.rb +73 -0
- data/spec/duck_testing/violation_spec.rb +58 -0
- data/spec/duck_testing/yard/builder_spec.rb +179 -0
- data/spec/duck_testing/yard/parser_spec.rb +38 -0
- data/spec/duck_type_integration_spec.rb +89 -0
- data/spec/hash_type_integration_spec.rb +112 -0
- data/spec/order_dependent_array_type_integration_spec.rb +121 -0
- data/spec/order_independent_array_type_integration_spec.rb +104 -0
- data/spec/spec_helper.rb +78 -0
- metadata +212 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
describe DuckTesting::Tester do
|
2
|
+
let(:tester) { described_class.new(*constract_param) }
|
3
|
+
let(:constract_param) { [receiver, method_name] }
|
4
|
+
let(:receiver) { nil }
|
5
|
+
let(:method_name) { nil }
|
6
|
+
|
7
|
+
shared_context "param matches to expected_types", match: true do
|
8
|
+
let(:param) { 1 }
|
9
|
+
let(:expected_types) do
|
10
|
+
[
|
11
|
+
DuckTesting::Type::ClassInstance.new(Fixnum),
|
12
|
+
DuckTesting::Type::ClassInstance.new(Float)
|
13
|
+
]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
shared_context "param does not match to expected_types", match: false do
|
18
|
+
let(:param) { 1 }
|
19
|
+
let(:expected_types) do
|
20
|
+
[DuckTesting::Type::ClassInstance.new(Float)]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#test_param" do
|
25
|
+
subject { tester.test_param(param, expected_types) }
|
26
|
+
|
27
|
+
context "when param matches to expected_types", match: true do
|
28
|
+
it "does not raise error" do
|
29
|
+
expect { subject }.not_to raise_error
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "when param does not match to expected_types", match: false do
|
34
|
+
it "raise ContractViolationError" do
|
35
|
+
expect { subject }.to raise_error(DuckTesting::ContractViolationError)
|
36
|
+
expect { subject }.to raise_error(/Contract violation for argument/)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#test_return" do
|
42
|
+
subject { tester.test_return(param, expected_types) }
|
43
|
+
|
44
|
+
context "when param matches to expected_types", match: true do
|
45
|
+
it "does not raise error" do
|
46
|
+
expect { subject }.not_to raise_error
|
47
|
+
end
|
48
|
+
|
49
|
+
it "returns the given param" do
|
50
|
+
should eq param
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when param does not match to expected_types", match: false do
|
55
|
+
it "raise ContractViolationError" do
|
56
|
+
expect { subject }.to raise_error(DuckTesting::ContractViolationError)
|
57
|
+
expect { subject }.to raise_error(/Contract violation for return value/)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#match?" do
|
63
|
+
subject { tester.match?(param, expected_types) }
|
64
|
+
|
65
|
+
context "when param is a one of expected_types", match: true do
|
66
|
+
it { should be true }
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when param is not a kind of expected_types", match: false do
|
70
|
+
it { should be false }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
describe DuckTesting::Violation do
|
2
|
+
let(:violation) { described_class.new(params) }
|
3
|
+
let(:params) do
|
4
|
+
{ call_data: call_data,
|
5
|
+
param: param,
|
6
|
+
expected_types: expected_types,
|
7
|
+
param_or_return: param_or_return }
|
8
|
+
end
|
9
|
+
let(:call_data) { nil }
|
10
|
+
let(:param) { nil }
|
11
|
+
let(:expected_types) { nil }
|
12
|
+
let(:param_or_return) { nil }
|
13
|
+
|
14
|
+
describe "#param?" do
|
15
|
+
subject { violation.param? }
|
16
|
+
|
17
|
+
context "when param_or_return is :param" do
|
18
|
+
let(:param_or_return) { :param }
|
19
|
+
|
20
|
+
it { should be true }
|
21
|
+
end
|
22
|
+
|
23
|
+
context "when param_or_return is :return" do
|
24
|
+
let(:param_or_return) { :return }
|
25
|
+
|
26
|
+
it { should be false }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#return?" do
|
31
|
+
subject { violation.return? }
|
32
|
+
|
33
|
+
context "when param_or_return is :param" do
|
34
|
+
let(:param_or_return) { :param }
|
35
|
+
|
36
|
+
it { should be false }
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when param_or_return is :return" do
|
40
|
+
let(:param_or_return) { :return }
|
41
|
+
|
42
|
+
it { should be true }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#expected" do
|
47
|
+
subject { violation.expected }
|
48
|
+
|
49
|
+
let(:expected_types) do
|
50
|
+
[
|
51
|
+
DuckTesting::Type::ClassInstance.new(Fixnum),
|
52
|
+
DuckTesting::Type::ClassInstance.new(Float)
|
53
|
+
]
|
54
|
+
end
|
55
|
+
|
56
|
+
it { should eq "Fixnum, Float" }
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
describe DuckTesting::YARD::Builder do
|
2
|
+
describe "#build" do
|
3
|
+
subject { builder.build(scope) }
|
4
|
+
let(:builder) { described_class.new(class_object) }
|
5
|
+
let(:class_object) { DuckTesting::YARD::Parser.parse_string(content).first }
|
6
|
+
let(:params) { [] }
|
7
|
+
|
8
|
+
context "when scope parameter is :instance" do
|
9
|
+
before { klass.send(:prepend, subject) }
|
10
|
+
let(:contents) { "Hello, World" }
|
11
|
+
let(:instance) { klass.new }
|
12
|
+
let(:run) { instance.bar(*params) }
|
13
|
+
let(:scope) { :instance }
|
14
|
+
|
15
|
+
context "and return tag comment is given" do
|
16
|
+
let(:content) do
|
17
|
+
<<-EOF
|
18
|
+
class Foo
|
19
|
+
# @!method bar
|
20
|
+
# @return [String]
|
21
|
+
end
|
22
|
+
EOF
|
23
|
+
end
|
24
|
+
|
25
|
+
context "and corresponding method returns valid type object" do
|
26
|
+
let(:klass) do
|
27
|
+
Class.new { def bar; "String" end }
|
28
|
+
end
|
29
|
+
it { expect { run }.not_to raise_error }
|
30
|
+
end
|
31
|
+
|
32
|
+
context "and corresponding method returns invalid type object" do
|
33
|
+
let(:klass) do
|
34
|
+
Class.new { def bar; :Symbol end }
|
35
|
+
end
|
36
|
+
it { expect { run }.to raise_error(DuckTesting::ContractViolationError) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "and param tag comment is given" do
|
41
|
+
context "and there is a parameter without default value" do
|
42
|
+
let(:content) do
|
43
|
+
<<-EOF
|
44
|
+
class Foo
|
45
|
+
# @!method bar
|
46
|
+
# @param name [String]
|
47
|
+
end
|
48
|
+
EOF
|
49
|
+
end
|
50
|
+
let(:klass) do
|
51
|
+
Class.new { def bar(_name) end }
|
52
|
+
end
|
53
|
+
context "and valid type object is given as the parameter" do
|
54
|
+
let(:params) { ["String"] }
|
55
|
+
it { expect { run }.not_to raise_error }
|
56
|
+
end
|
57
|
+
|
58
|
+
context "and invalid type object is given as the parameter" do
|
59
|
+
let(:params) { [:String] }
|
60
|
+
it { expect { run }.to raise_error(DuckTesting::ContractViolationError) }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "and there is a parameter with default value" do
|
65
|
+
let(:content) do
|
66
|
+
<<-EOF
|
67
|
+
class Foo
|
68
|
+
# @!method bar
|
69
|
+
# @param name [String]
|
70
|
+
end
|
71
|
+
EOF
|
72
|
+
end
|
73
|
+
let(:klass) do
|
74
|
+
Class.new { def bar(_name = nil) end }
|
75
|
+
end
|
76
|
+
|
77
|
+
context "and the parameter is omitted" do
|
78
|
+
it { expect { run }.not_to raise_error }
|
79
|
+
end
|
80
|
+
|
81
|
+
context "and valid type object is given as the parameter" do
|
82
|
+
let(:params) { ["String"] }
|
83
|
+
it { expect { run }.not_to raise_error }
|
84
|
+
end
|
85
|
+
|
86
|
+
context "and invalid type object is given as the parameter" do
|
87
|
+
let(:params) { [:String] }
|
88
|
+
it { expect { run }.to raise_error(DuckTesting::ContractViolationError) }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "and there is a keyword parameter" do
|
93
|
+
let(:content) do
|
94
|
+
<<-EOF
|
95
|
+
class Foo
|
96
|
+
# @param key [String]
|
97
|
+
def bar(key: nil) end
|
98
|
+
end
|
99
|
+
EOF
|
100
|
+
end
|
101
|
+
let(:klass) do
|
102
|
+
Class.new { def bar(key: nil) end }
|
103
|
+
end
|
104
|
+
|
105
|
+
context "and valid type object is given as the parameter" do
|
106
|
+
let(:params) { [key: "String"] }
|
107
|
+
it { expect { run }.not_to raise_error }
|
108
|
+
end
|
109
|
+
|
110
|
+
context "and invalid type object is given as the parameter" do
|
111
|
+
let(:params) { [key: :Symbol] }
|
112
|
+
it { expect { run }.to raise_error(DuckTesting::ContractViolationError) }
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context "and there are both an normal paramter and a keyword parameter" do
|
117
|
+
let(:content) do
|
118
|
+
<<-EOF
|
119
|
+
class Foo
|
120
|
+
# @param name [String]
|
121
|
+
# @param key [String]
|
122
|
+
def bar(name, key: nil) end
|
123
|
+
end
|
124
|
+
EOF
|
125
|
+
end
|
126
|
+
let(:klass) do
|
127
|
+
Class.new { def bar(_name, key: nil) end }
|
128
|
+
end
|
129
|
+
|
130
|
+
context "and valid type objects are given as those parameters" do
|
131
|
+
let(:params) { ["String", key: "String"] }
|
132
|
+
it { expect { run }.not_to raise_error }
|
133
|
+
end
|
134
|
+
|
135
|
+
context "and invalid type object is given as the normal parameter" do
|
136
|
+
let(:params) { [:Symbol, key: "String"] }
|
137
|
+
it { expect { run }.to raise_error(DuckTesting::ContractViolationError) }
|
138
|
+
end
|
139
|
+
|
140
|
+
context "and invalid type object is given as the keyword parameter" do
|
141
|
+
let(:params) { ["String", key: :Symbol] }
|
142
|
+
it { expect { run }.to raise_error(DuckTesting::ContractViolationError) }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "when scope parameter is :class" do
|
149
|
+
before { klass.singleton_class.send(:prepend, subject) }
|
150
|
+
let(:run) { klass.bar(*params) }
|
151
|
+
let(:scope) { :class }
|
152
|
+
|
153
|
+
context "and return tag comment is given" do
|
154
|
+
let(:content) do
|
155
|
+
<<-EOF
|
156
|
+
class Foo
|
157
|
+
# @!method self.bar
|
158
|
+
# @return [String]
|
159
|
+
end
|
160
|
+
EOF
|
161
|
+
end
|
162
|
+
|
163
|
+
context "and corresponding method returns valid type object" do
|
164
|
+
let(:klass) do
|
165
|
+
Class.new { def self.bar; "String" end }
|
166
|
+
end
|
167
|
+
it { expect { run }.not_to raise_error }
|
168
|
+
end
|
169
|
+
|
170
|
+
context "and corresponding method returns invalid type object" do
|
171
|
+
let(:klass) do
|
172
|
+
Class.new { def self.bar; :Symbol end }
|
173
|
+
end
|
174
|
+
it { expect { run }.to raise_error(DuckTesting::ContractViolationError) }
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
describe DuckTesting::YARD::Parser do
|
2
|
+
describe ".parse_string" do
|
3
|
+
subject { described_class.parse_string(content) }
|
4
|
+
let(:content) do
|
5
|
+
<<-EOF
|
6
|
+
class Foo
|
7
|
+
# Reverses the contents of a String or IO object.
|
8
|
+
#
|
9
|
+
# @param [String, #read] contents the contents to reverse
|
10
|
+
# @return [String] the contents reversed lexically
|
11
|
+
def reverse(contents)
|
12
|
+
contents = contents.read if contents.respond_to? :read
|
13
|
+
contents.reverse
|
14
|
+
end
|
15
|
+
end
|
16
|
+
EOF
|
17
|
+
end
|
18
|
+
|
19
|
+
it "returns an array of DuckTesting::YARD::ClassObject" do
|
20
|
+
should all be_a DuckTesting::YARD::ClassObject
|
21
|
+
|
22
|
+
class_object = subject.first
|
23
|
+
expect(class_object.path).to eq "Foo"
|
24
|
+
expect(class_object.method_objects).to all be_a DuckTesting::YARD::MethodObject
|
25
|
+
|
26
|
+
method_object = class_object.method_objects.first
|
27
|
+
expect(method_object.path).to eq "Foo#reverse"
|
28
|
+
expect(method_object.method_parameters).to all be_a DuckTesting::YARD::MethodParameter
|
29
|
+
|
30
|
+
method_parameter = method_object.method_parameters.first
|
31
|
+
expect(method_parameter.name).to eq "contents"
|
32
|
+
expect(method_parameter.parameter_tag.types).to eq ["String", "#read"]
|
33
|
+
|
34
|
+
return_tag = method_object.return_tag
|
35
|
+
expect(return_tag.types).to eq ["String"]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require "stringio"
|
2
|
+
|
3
|
+
describe "Duck type integration spec" do
|
4
|
+
subject do
|
5
|
+
instance.read(param)
|
6
|
+
end
|
7
|
+
|
8
|
+
before do
|
9
|
+
klass.send(:prepend, duck_testing_module)
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:instance) { klass.new }
|
13
|
+
|
14
|
+
let(:klass) do
|
15
|
+
Class.new do
|
16
|
+
# @param io [#read]
|
17
|
+
# @return [#read]
|
18
|
+
def read(io)
|
19
|
+
io
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when expected parameter and return are given" do
|
25
|
+
let(:param) { StringIO.new }
|
26
|
+
|
27
|
+
let(:duck_testing_module) do
|
28
|
+
Module.new do
|
29
|
+
def read(io)
|
30
|
+
tester = DuckTesting::Tester.new(self, "read")
|
31
|
+
tester.test_param(io, [
|
32
|
+
DuckTesting::Type::DuckType.new("read")
|
33
|
+
])
|
34
|
+
tester.test_return(super, [
|
35
|
+
DuckTesting::Type::DuckType.new("read")
|
36
|
+
])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it "does not raise error" do
|
42
|
+
expect { subject }.not_to raise_error
|
43
|
+
end
|
44
|
+
|
45
|
+
it "returns original result" do
|
46
|
+
should eq param
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "when unexpected parameter is given" do
|
51
|
+
let(:param) { "string" }
|
52
|
+
|
53
|
+
let(:duck_testing_module) do
|
54
|
+
Module.new do
|
55
|
+
def read(io)
|
56
|
+
tester = DuckTesting::Tester.new(self, "read")
|
57
|
+
tester.test_param(io, [
|
58
|
+
DuckTesting::Type::DuckType.new("read")
|
59
|
+
])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it "raises ContractViolationError" do
|
65
|
+
expect { subject }.to raise_error(DuckTesting::ContractViolationError)
|
66
|
+
expect { subject }.to raise_error(/Contract violation for argument/)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "when unexpected result is given" do
|
71
|
+
let(:param) { "string" }
|
72
|
+
|
73
|
+
let(:duck_testing_module) do
|
74
|
+
Module.new do
|
75
|
+
def read(io)
|
76
|
+
tester = DuckTesting::Tester.new(self, "read")
|
77
|
+
tester.test_return(super, [
|
78
|
+
DuckTesting::Type::DuckType.new("read")
|
79
|
+
])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it "raises ContractViolationError" do
|
85
|
+
expect { subject }.to raise_error(DuckTesting::ContractViolationError)
|
86
|
+
expect { subject }.to raise_error(/Contract violation for return value/)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
describe "Hash type integration spec" do
|
2
|
+
subject do
|
3
|
+
instance.get(*params)
|
4
|
+
end
|
5
|
+
|
6
|
+
before do
|
7
|
+
klass.send(:prepend, duck_testing_module)
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:instance) { klass.new }
|
11
|
+
|
12
|
+
let(:klass) do
|
13
|
+
Class.new do
|
14
|
+
# @param hash [Hash{Symbol => Object}]
|
15
|
+
# @param key [Symbol]
|
16
|
+
# @param default [Object]
|
17
|
+
# @return [Object]
|
18
|
+
def get(hash, key, default = nil)
|
19
|
+
hash.key?(key) ? hash[key] : default
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when expected parameter and return are given" do
|
25
|
+
let(:params) { [{ a: 1, b: 2 }, :b, "hello"] }
|
26
|
+
|
27
|
+
let(:duck_testing_module) do
|
28
|
+
Module.new do
|
29
|
+
def get(hash, key, default = nil)
|
30
|
+
tester = DuckTesting::Tester.new(self, "get")
|
31
|
+
tester.test_param(hash, [
|
32
|
+
DuckTesting::Type::Hash.new(
|
33
|
+
[DuckTesting::Type::ClassInstance.new(Symbol)],
|
34
|
+
[DuckTesting::Type::ClassInstance.new(Object)]
|
35
|
+
)
|
36
|
+
])
|
37
|
+
tester.test_param(key, [
|
38
|
+
DuckTesting::Type::ClassInstance.new(Symbol)
|
39
|
+
])
|
40
|
+
tester.test_param(default, [
|
41
|
+
DuckTesting::Type::ClassInstance.new(Object)
|
42
|
+
])
|
43
|
+
tester.test_return(super, [
|
44
|
+
DuckTesting::Type::ClassInstance.new(Object)
|
45
|
+
])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it "does not raise error" do
|
51
|
+
expect { subject }.not_to raise_error
|
52
|
+
end
|
53
|
+
|
54
|
+
it "returns original result" do
|
55
|
+
should eq 2
|
56
|
+
end
|
57
|
+
end
|
58
|
+
context "when unexpected parameter is given" do
|
59
|
+
let(:params) { [{ "a" => 1 }, "a"] }
|
60
|
+
|
61
|
+
let(:duck_testing_module) do
|
62
|
+
Module.new do
|
63
|
+
def get(hash, _key, _default = nil)
|
64
|
+
tester = DuckTesting::Tester.new(self, "get")
|
65
|
+
tester.test_param(hash, [
|
66
|
+
DuckTesting::Type::Hash.new(
|
67
|
+
[DuckTesting::Type::ClassInstance.new(Symbol)],
|
68
|
+
[DuckTesting::Type::ClassInstance.new(Object)]
|
69
|
+
)
|
70
|
+
])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it "raises ContractViolationError" do
|
76
|
+
expect { subject }.to raise_error(DuckTesting::ContractViolationError)
|
77
|
+
expect { subject }.to raise_error(/Contract violation for argument/)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "when unexpected result is given" do
|
82
|
+
let(:params) { [{ a: 1, b: 2 }, :b, "hello"] }
|
83
|
+
|
84
|
+
let(:duck_testing_module) do
|
85
|
+
Module.new do
|
86
|
+
def get(hash, key, default = nil)
|
87
|
+
tester = DuckTesting::Tester.new(self, "get")
|
88
|
+
tester.test_param(hash, [
|
89
|
+
DuckTesting::Type::Hash.new(
|
90
|
+
[DuckTesting::Type::ClassInstance.new(Symbol)],
|
91
|
+
[DuckTesting::Type::ClassInstance.new(Object)]
|
92
|
+
)
|
93
|
+
])
|
94
|
+
tester.test_param(key, [
|
95
|
+
DuckTesting::Type::ClassInstance.new(Symbol)
|
96
|
+
])
|
97
|
+
tester.test_param(default, [
|
98
|
+
DuckTesting::Type::ClassInstance.new(Object)
|
99
|
+
])
|
100
|
+
tester.test_return(super, [
|
101
|
+
DuckTesting::Type::ClassInstance.new(Symbol)
|
102
|
+
])
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
it "raises ContractViolationError" do
|
108
|
+
expect { subject }.to raise_error(DuckTesting::ContractViolationError)
|
109
|
+
expect { subject }.to raise_error(/Contract violation for return value/)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|