bogus 0.1.0 → 0.1.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.
- data/.travis.yml +1 -0
- data/Gemfile.lock +6 -1
- data/README.md +3 -0
- data/features/changelog.md +30 -22
- data/features/configuration/fake_ar_attributes.feature +3 -1
- data/features/configuration/search_modules.feature +5 -0
- data/features/contract_tests/contract_tests_mocks.feature +12 -1
- data/features/contract_tests/contract_tests_spies.feature +14 -2
- data/features/contract_tests/contract_tests_stubs.feature +13 -2
- data/features/contract_tests/return_value_contracts.feature +15 -4
- data/features/fakes/anonymous_doubles.feature +15 -1
- data/features/fakes/duck_types.feature +12 -2
- data/features/fakes/fake_objects.feature +11 -1
- data/features/fakes/global_fake_configuration.feature +4 -1
- data/features/readme.md +2 -2
- data/features/safe_stubbing/argument_matchers.feature +32 -2
- data/features/safe_stubbing/safe_mocking.feature +9 -2
- data/features/safe_stubbing/safe_stubbing.feature +11 -2
- data/features/safe_stubbing/spies.feature +52 -1
- data/features/step_definitions/rspec_steps.rb +1 -2
- data/features/support/env.rb +19 -0
- data/lib/bogus/active_record_accessors.rb +4 -4
- data/lib/bogus/adds_recording.rb +1 -0
- data/lib/bogus/any_args.rb +32 -3
- data/lib/bogus/class_methods.rb +7 -1
- data/lib/bogus/copies_classes.rb +5 -2
- data/lib/bogus/fake.rb +0 -2
- data/lib/bogus/have_received_matcher.rb +5 -0
- data/lib/bogus/interaction.rb +66 -35
- data/lib/bogus/interactions_repository.rb +1 -1
- data/lib/bogus/makes_substitute_methods.rb +3 -2
- data/lib/bogus/mocking_dsl.rb +8 -0
- data/lib/bogus/public_methods.rb +1 -1
- data/lib/bogus/rspec_extensions.rb +8 -1
- data/lib/bogus/same_class.rb +14 -0
- data/lib/bogus/shadow.rb +8 -12
- data/lib/bogus/verifies_stub_definition.rb +1 -5
- data/lib/bogus/version.rb +1 -1
- data/spec/bogus/adds_recording_spec.rb +8 -0
- data/spec/bogus/class_methods_spec.rb +8 -2
- data/spec/bogus/clean_ruby_spec.rb +15 -0
- data/spec/bogus/have_received_matcher_spec.rb +45 -27
- data/spec/bogus/instance_methods_spec.rb +1 -1
- data/spec/bogus/interaction_spec.rb +87 -83
- data/spec/bogus/interactions_repository_spec.rb +8 -11
- data/spec/bogus/shadow_spec.rb +4 -1
- metadata +81 -39
- checksums.yaml +0 -7
data/lib/bogus/adds_recording.rb
CHANGED
data/lib/bogus/any_args.rb
CHANGED
@@ -1,7 +1,36 @@
|
|
1
1
|
module Bogus
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
class WithArguments
|
3
|
+
attr_reader :predicate
|
4
|
+
|
5
|
+
def initialize(&predicate)
|
6
|
+
@predicate = predicate
|
5
7
|
end
|
8
|
+
|
9
|
+
def matches?(args)
|
10
|
+
predicate.call(*args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.matches?(opts = {})
|
14
|
+
stubbed = opts.fetch(:stubbed)
|
15
|
+
recorded = opts.fetch(:recorded)
|
16
|
+
return false unless with_matcher?(stubbed)
|
17
|
+
return extract(stubbed).matches?(recorded)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.with_matcher?(args)
|
21
|
+
args.first.is_a?(WithArguments)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def self.extract(args)
|
27
|
+
args.first
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
AnyArgs = WithArguments.new{ true }
|
32
|
+
|
33
|
+
def AnyArgs.inspect
|
34
|
+
"any_args"
|
6
35
|
end
|
7
36
|
end
|
data/lib/bogus/class_methods.rb
CHANGED
@@ -4,7 +4,7 @@ module Bogus
|
|
4
4
|
takes :klass
|
5
5
|
|
6
6
|
def all
|
7
|
-
klass.methods - Class.methods
|
7
|
+
klass.methods - Class.methods - bogus_methods
|
8
8
|
end
|
9
9
|
|
10
10
|
def get(name)
|
@@ -18,5 +18,11 @@ module Bogus
|
|
18
18
|
def define(body)
|
19
19
|
klass.instance_eval(body)
|
20
20
|
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def bogus_methods
|
25
|
+
[:__shadow__, :__reset__, :__overwrite__, :__overwritten_methods__, :__record__]
|
26
|
+
end
|
21
27
|
end
|
22
28
|
end
|
data/lib/bogus/copies_classes.rb
CHANGED
@@ -5,8 +5,11 @@ module Bogus
|
|
5
5
|
takes :copies_methods
|
6
6
|
|
7
7
|
def copy(klass)
|
8
|
-
copy_class = Class.new(Bogus::Fake)
|
9
|
-
|
8
|
+
copy_class = Class.new(Bogus::Fake) do
|
9
|
+
define_singleton_method(:__copied_class__) do
|
10
|
+
klass
|
11
|
+
end
|
12
|
+
end
|
10
13
|
copies_methods.copy(klass, copy_class)
|
11
14
|
copy_class
|
12
15
|
end
|
data/lib/bogus/fake.rb
CHANGED
data/lib/bogus/interaction.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
module Bogus
|
2
2
|
class Interaction < Struct.new(:method, :args, :return_value, :error, :has_result)
|
3
|
+
attr_accessor :arguments
|
4
|
+
|
5
|
+
def self.same?(opts = {})
|
6
|
+
InteractionComparator.new(opts).same?
|
7
|
+
end
|
8
|
+
|
3
9
|
def initialize(method, args, &block)
|
4
10
|
self.method = method
|
5
11
|
self.args = args
|
@@ -10,53 +16,78 @@ module Bogus
|
|
10
16
|
end
|
11
17
|
end
|
12
18
|
|
13
|
-
|
14
|
-
method == other.method && same_args?(other) && same_result?(other)
|
15
|
-
end
|
19
|
+
private
|
16
20
|
|
17
|
-
def
|
18
|
-
|
21
|
+
def evaluate_return_value(block)
|
22
|
+
self.return_value = block.call
|
23
|
+
rescue => e
|
24
|
+
self.error = e.class
|
19
25
|
end
|
20
26
|
|
21
|
-
|
22
|
-
|
23
|
-
args.reject { |arg| arg.eql?(DefaultValue) }
|
24
|
-
end
|
27
|
+
class InteractionComparator
|
28
|
+
attr_reader :recorded, :stubbed
|
25
29
|
|
26
|
-
|
30
|
+
def initialize(opts = {})
|
31
|
+
@recorded = opts.fetch(:recorded)
|
32
|
+
@stubbed = opts.fetch(:stubbed)
|
33
|
+
end
|
27
34
|
|
28
|
-
|
29
|
-
|
35
|
+
def same?
|
36
|
+
return false unless recorded.method == stubbed.method
|
37
|
+
return false unless same_result?
|
38
|
+
same_args?
|
39
|
+
end
|
30
40
|
|
31
|
-
|
32
|
-
return false unless args.size == other_args.size
|
33
|
-
args.zip(other_args).all?{|a1, a2| a1 == a2 || a2 == a1}
|
34
|
-
end
|
41
|
+
private
|
35
42
|
|
36
|
-
|
37
|
-
|
38
|
-
other_args + [{}]
|
39
|
-
else
|
40
|
-
other_args
|
43
|
+
def same_args?
|
44
|
+
ArgumentComparator.new(recorded: recorded.args, stubbed: stubbed.args).same?
|
41
45
|
end
|
42
|
-
end
|
43
46
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
+
def same_result?
|
48
|
+
return true unless recorded.has_result && stubbed.has_result
|
49
|
+
recorded.return_value == stubbed.return_value && recorded.error == stubbed.error
|
50
|
+
end
|
47
51
|
end
|
48
52
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
53
|
+
class ArgumentComparator
|
54
|
+
attr_reader :recorded, :stubbed
|
55
|
+
|
56
|
+
def initialize(opts = {})
|
57
|
+
@recorded = opts.fetch(:recorded)
|
58
|
+
@stubbed = opts.fetch(:stubbed)
|
59
|
+
end
|
60
|
+
|
61
|
+
def same?
|
62
|
+
return true if with_matcher_args?
|
63
|
+
|
64
|
+
stubbed == recorded_without_defaults
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def recorded_without_defaults
|
70
|
+
without_defaults = recorded.reject{|v| DefaultValue == v}
|
71
|
+
remove_default_keywords(without_defaults)
|
72
|
+
end
|
73
|
+
|
74
|
+
def remove_default_keywords(recorded)
|
75
|
+
return recorded unless recorded_has_keyword?
|
76
|
+
positional = recorded[0...-1]
|
77
|
+
keyword = recorded.last
|
78
|
+
without_defaults = keyword.reject{|_, v| DefaultValue == v}
|
79
|
+
return positional if without_defaults.empty?
|
80
|
+
positional + [without_defaults]
|
81
|
+
end
|
82
|
+
|
83
|
+
def with_matcher_args?
|
84
|
+
WithArguments.matches?(stubbed: stubbed, recorded: recorded_without_defaults)
|
85
|
+
end
|
54
86
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
arg
|
87
|
+
def recorded_has_keyword?
|
88
|
+
last_recorded = recorded.last
|
89
|
+
return false unless last_recorded.is_a?(Hash)
|
90
|
+
last_recorded.values.any? { |v| DefaultValue == v }
|
60
91
|
end
|
61
92
|
end
|
62
93
|
end
|
@@ -7,8 +7,9 @@ module Bogus
|
|
7
7
|
def stringify(method)
|
8
8
|
args = method_stringifier.argument_values(method.parameters)
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
send_args = [method.name.inspect, args].reject(&:empty?).join(', ')
|
11
|
+
|
12
|
+
method_stringifier.stringify(method, "__record__(#{send_args})")
|
12
13
|
end
|
13
14
|
end
|
14
15
|
end
|
data/lib/bogus/mocking_dsl.rb
CHANGED
data/lib/bogus/public_methods.rb
CHANGED
@@ -11,8 +11,15 @@ module Bogus
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def verify_contract(name)
|
14
|
+
old_described_class = described_class
|
15
|
+
|
14
16
|
before do
|
15
|
-
Bogus.record_calls_for(name, described_class)
|
17
|
+
new_class = Bogus.record_calls_for(name, described_class)
|
18
|
+
example.metadata[:example_group][:described_class] = new_class
|
19
|
+
end
|
20
|
+
|
21
|
+
after do
|
22
|
+
example.metadata[:example_group][:described_class] = old_described_class
|
16
23
|
end
|
17
24
|
|
18
25
|
RSpec.configure do |config|
|
data/lib/bogus/shadow.rb
CHANGED
@@ -5,7 +5,6 @@ module Bogus
|
|
5
5
|
def initialize
|
6
6
|
@calls = []
|
7
7
|
@stubs = []
|
8
|
-
@defaults = {}
|
9
8
|
@required = Set.new
|
10
9
|
end
|
11
10
|
|
@@ -16,14 +15,13 @@ module Bogus
|
|
16
15
|
end
|
17
16
|
|
18
17
|
def has_received(name, args)
|
19
|
-
@calls.
|
18
|
+
@calls.any? { |i| Interaction.same?(recorded: i, stubbed: Interaction.new(name, args)) }
|
20
19
|
end
|
21
20
|
|
22
21
|
def stubs(name, *args, &return_value)
|
23
22
|
interaction = Interaction.new(name, args)
|
24
23
|
add_stub(interaction, return_value)
|
25
|
-
|
26
|
-
@required.delete(interaction)
|
24
|
+
@required.reject! { |i| Interaction.same?(recorded: i, stubbed: interaction) }
|
27
25
|
interaction
|
28
26
|
end
|
29
27
|
|
@@ -33,7 +31,11 @@ module Bogus
|
|
33
31
|
end
|
34
32
|
|
35
33
|
def unsatisfied_interactions
|
36
|
-
@required.reject
|
34
|
+
@required.reject do |stubbed|
|
35
|
+
@calls.any? do |recorded|
|
36
|
+
Interaction.same?(recorded: recorded, stubbed: stubbed)
|
37
|
+
end
|
38
|
+
end
|
37
39
|
end
|
38
40
|
|
39
41
|
def self.has_shadow?(object)
|
@@ -42,18 +44,12 @@ module Bogus
|
|
42
44
|
|
43
45
|
private
|
44
46
|
|
45
|
-
def override_default(interaction, return_value)
|
46
|
-
return unless interaction.any_args?
|
47
|
-
@defaults[interaction.method] = return_value || proc{nil}
|
48
|
-
end
|
49
|
-
|
50
47
|
def add_stub(interaction, return_value_block)
|
51
48
|
@stubs << [interaction, return_value_block] if return_value_block
|
52
49
|
end
|
53
50
|
|
54
51
|
def return_value(interaction)
|
55
|
-
_, return_value = @stubs.reverse.find{|i, v|
|
56
|
-
return_value ||= @defaults[interaction.method]
|
52
|
+
_, return_value = @stubs.reverse.find{|i, v| Interaction.same?(recorded: interaction, stubbed: i)}
|
57
53
|
return_value ||= proc{ UndefinedReturnValue.new(interaction) }
|
58
54
|
return_value.call
|
59
55
|
end
|
@@ -7,7 +7,7 @@ module Bogus
|
|
7
7
|
def verify!(object, method_name, args)
|
8
8
|
stubbing_non_existent_method!(object, method_name) unless object.respond_to?(method_name)
|
9
9
|
return unless object.methods.include?(method_name)
|
10
|
-
return if
|
10
|
+
return if WithArguments.with_matcher?(args)
|
11
11
|
method = object.method(method_name)
|
12
12
|
verify_call!(method, args)
|
13
13
|
end
|
@@ -31,9 +31,5 @@ module Bogus
|
|
31
31
|
def stubbing_non_existent_method!(object, method_name)
|
32
32
|
raise NameError, "#{object.inspect} does not respond to #{method_name}"
|
33
33
|
end
|
34
|
-
|
35
|
-
def any_args?(args)
|
36
|
-
[Bogus::AnyArgs] == args
|
37
|
-
end
|
38
34
|
end
|
39
35
|
end
|
data/lib/bogus/version.rb
CHANGED
@@ -43,6 +43,10 @@ describe Bogus::AddsRecording do
|
|
43
43
|
it "records the overwritten class, so that it can be later restored" do
|
44
44
|
overwritten_classes.should have_received.add("SampleModule::Library", SampleModule::Library)
|
45
45
|
end
|
46
|
+
|
47
|
+
it "returns the proxy class" do
|
48
|
+
adds_recording.add(:library).should == Object
|
49
|
+
end
|
46
50
|
end
|
47
51
|
|
48
52
|
context "with class argument" do
|
@@ -65,5 +69,9 @@ describe Bogus::AddsRecording do
|
|
65
69
|
it "records the overwritten class, so that it can be later restored" do
|
66
70
|
overwritten_classes.should have_received.add("SampleModule::OtherClass", SampleModule::OtherClass)
|
67
71
|
end
|
72
|
+
|
73
|
+
it "returns the proxy class" do
|
74
|
+
adds_recording.add(:library, SampleModule::OtherClass).should == Object
|
75
|
+
end
|
68
76
|
end
|
69
77
|
end
|
@@ -11,12 +11,18 @@ module Bogus
|
|
11
11
|
|
12
12
|
def self.hello
|
13
13
|
end
|
14
|
+
|
15
|
+
def self.__shadow__; end
|
16
|
+
def self.__reset__; end
|
17
|
+
def self.__overwrite__; end
|
18
|
+
def self.__overwritten_methods__; end
|
19
|
+
def self.__record__; end
|
14
20
|
end
|
15
21
|
|
16
22
|
let(:class_methods) { ClassMethods.new(SampleClass) }
|
17
23
|
|
18
|
-
it "lists the
|
19
|
-
class_methods.all.should
|
24
|
+
it "lists the class methods excluding the ones added by Bogus" do
|
25
|
+
class_methods.all.should =~ [:bar, :hello]
|
20
26
|
end
|
21
27
|
|
22
28
|
it "returns the instance methods by name" do
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Ruby syntax" do
|
4
|
+
it "is clean" do
|
5
|
+
bogus_warnings.should == []
|
6
|
+
end
|
7
|
+
|
8
|
+
def bogus_warnings
|
9
|
+
warnings.select { |w| w =~ %r{lib/bogus} }
|
10
|
+
end
|
11
|
+
|
12
|
+
def warnings
|
13
|
+
`ruby -w lib/bogus.rb 2>&1`.split("\n")
|
14
|
+
end
|
15
|
+
end
|