bogus 0.0.2 → 0.0.3.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/Gemfile.lock +14 -2
- data/Guardfile +5 -0
- data/README.md +7 -249
- data/bogus.gemspec +3 -1
- data/features/.nav +21 -0
- data/features/authors.md +42 -0
- data/{CHANGELOG.md → features/changelog.md} +9 -2
- data/features/configuration_options.feature +1 -2
- data/features/{contract_tests_mocks.feature → contract_tests/contract_tests_mocks.feature} +4 -1
- data/features/{contract_tests_spies.feature → contract_tests/contract_tests_spies.feature} +2 -0
- data/features/{contract_tests_stubs.feature → contract_tests/contract_tests_stubs.feature} +14 -0
- data/features/contract_tests/readme.md +26 -0
- data/features/{return_value_contracts.feature → contract_tests/return_value_contracts.feature} +6 -2
- data/features/{anonymous_doubles.feature → fakes/anonymous_doubles.feature} +24 -8
- data/features/{fake_objects.feature → fakes/fake_objects.feature} +39 -4
- data/features/fakes/global_fake_configuration.feature +88 -0
- data/features/fakes/readme.md +11 -0
- data/features/fakes/replacing_classes.feature +148 -0
- data/features/getting_started.md +36 -0
- data/features/license.md +9 -0
- data/features/readme.md +173 -0
- data/features/safe_stubbing/argument_matchers.feature +30 -0
- data/features/safe_stubbing/readme.md +35 -0
- data/features/{safe_mocking.feature → safe_stubbing/safe_mocking.feature} +14 -0
- data/features/{safe_stubbing.feature → safe_stubbing/safe_stubbing.feature} +18 -4
- data/features/{spies.feature → safe_stubbing/spies.feature} +19 -4
- data/features/step_definitions/rspec_steps.rb +14 -5
- data/features/support/env.rb +4 -0
- data/lib/bogus.rb +2 -0
- data/lib/bogus/adds_recording.rb +11 -7
- data/lib/bogus/any_args.rb +7 -0
- data/lib/bogus/anything.rb +11 -0
- data/lib/bogus/contract_not_fulfilled.rb +17 -10
- data/lib/bogus/copies_classes.rb +2 -6
- data/lib/bogus/creates_fakes_with_stubbed_methods.rb +40 -0
- data/lib/bogus/double.rb +28 -8
- data/lib/bogus/ensures_all_interactions_satisfied.rb +36 -0
- data/lib/bogus/fake.rb +6 -0
- data/lib/bogus/fake_configuration.rb +69 -0
- data/lib/bogus/fakes_classes.rb +21 -0
- data/lib/bogus/has_overwritten_methods.rb +24 -0
- data/lib/bogus/have_received_matcher.rb +63 -0
- data/lib/bogus/injector.rb +36 -14
- data/lib/bogus/interaction.rb +33 -17
- data/lib/bogus/makes_substitute_methods.rb +15 -0
- data/lib/bogus/mocking_dsl.rb +31 -0
- data/lib/bogus/multi_stubber.rb +15 -0
- data/lib/bogus/not_all_expectations_satisfied.rb +27 -0
- data/lib/bogus/overwriten_classes.rb +15 -0
- data/lib/bogus/overwrites_classes.rb +2 -2
- data/lib/bogus/overwrites_methods.rb +42 -0
- data/lib/bogus/proxies_method_calls.rb +23 -0
- data/lib/bogus/proxy_class.rb +25 -16
- data/lib/bogus/public_methods.rb +36 -11
- data/lib/bogus/record_interactions.rb +3 -9
- data/lib/bogus/recording_proxy.rb +5 -0
- data/lib/bogus/registers_created_fakes.rb +2 -1
- data/lib/bogus/resets_overwritten_classes.rb +14 -0
- data/lib/bogus/resets_stubbed_methods.rb +12 -0
- data/lib/bogus/responds_to_everything.rb +11 -0
- data/lib/bogus/rspec.rb +7 -0
- data/lib/bogus/rspec_adapter.rb +17 -0
- data/lib/bogus/rspec_extensions.rb +8 -20
- data/lib/bogus/shadow.rb +60 -0
- data/lib/bogus/verifies_contracts.rb +5 -1
- data/lib/bogus/verifies_stub_definition.rb +5 -0
- data/lib/bogus/version.rb +1 -1
- data/lib/tracks_existence_of_test_doubles.rb +11 -0
- data/spec/bogus/adds_recording_spec.rb +46 -10
- data/spec/bogus/anything_spec.rb +13 -0
- data/spec/bogus/copies_classes_spec.rb +4 -3
- data/spec/bogus/creates_fakes_with_stubbed_methods_spec.rb +121 -0
- data/spec/bogus/double_spec.rb +63 -20
- data/spec/bogus/ensures_all_interactions_satisfied_spec.rb +43 -0
- data/spec/bogus/fake_configuration_spec.rb +99 -0
- data/spec/bogus/fakes_classes_spec.rb +46 -0
- data/spec/bogus/have_received_matcher_spec.rb +56 -0
- data/spec/bogus/interaction_spec.rb +18 -7
- data/spec/bogus/interactions_repository_spec.rb +42 -0
- data/spec/bogus/makes_substitute_methods_spec.rb +24 -0
- data/spec/bogus/mocking_dsl_spec.rb +234 -7
- data/spec/bogus/multi_stubber_spec.rb +31 -0
- data/spec/bogus/overwriten_classes_spec.rb +27 -0
- data/spec/bogus/overwrites_classes_spec.rb +2 -2
- data/spec/bogus/overwrites_methods_spec.rb +107 -0
- data/spec/bogus/proxy_class_spec.rb +6 -0
- data/spec/bogus/record_interactions_spec.rb +3 -4
- data/spec/bogus/registers_created_fakes_spec.rb +8 -0
- data/spec/bogus/resets_overwritten_classes_spec.rb +26 -0
- data/spec/bogus/resets_stubbed_methods_spec.rb +16 -0
- data/spec/bogus/shadow_spec.rb +182 -0
- data/spec/bogus/verifies_contracts_spec.rb +9 -3
- data/spec/bogus/verifies_stub_definition_spec.rb +4 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/fake_creator_of_fakes.rb +15 -0
- data/spec/support/sample_fake.rb +13 -0
- data/spec/tracks_existence_of_test_doubles_spec.rb +26 -0
- metadata +105 -32
- data/lib/bogus/creates_anonymous_stubs.rb +0 -27
- data/lib/bogus/invocation_matcher.rb +0 -27
- data/lib/bogus/rr_proxy.rb +0 -5
- data/spec/bogus/invocation_matcher_spec.rb +0 -26
data/lib/bogus/interaction.rb
CHANGED
@@ -1,24 +1,40 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module Bogus
|
2
|
+
class Interaction < Struct.new(:method, :args, :return_value, :error, :has_result)
|
3
|
+
def initialize(method, args, &block)
|
4
|
+
self.method = method
|
5
|
+
self.args = args
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
if block_given?
|
8
|
+
evaluate_return_value(block)
|
9
|
+
self.has_result = true
|
10
|
+
end
|
9
11
|
end
|
10
|
-
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
def ==(other)
|
14
|
+
method == other.method && same_args?(other) && same_result?(other)
|
15
|
+
end
|
16
|
+
|
17
|
+
def any_args?
|
18
|
+
args == [AnyArgs]
|
19
|
+
end
|
16
20
|
|
17
|
-
|
21
|
+
private
|
18
22
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
+
def same_args?(other)
|
24
|
+
return true if any_args? || other.any_args?
|
25
|
+
return false unless args.size == other.args.size
|
26
|
+
args.zip(other.args).all?{|a1, a2| a1 == a2 || a2 == a1}
|
27
|
+
end
|
28
|
+
|
29
|
+
def same_result?(other)
|
30
|
+
return true unless has_result && other.has_result
|
31
|
+
return_value == other.return_value && error == other.error
|
32
|
+
end
|
33
|
+
|
34
|
+
def evaluate_return_value(block)
|
35
|
+
self.return_value = block.call
|
36
|
+
rescue => e
|
37
|
+
self.error = e.class
|
38
|
+
end
|
23
39
|
end
|
24
40
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Bogus
|
2
|
+
class MakesSubstituteMethods
|
3
|
+
extend Takes
|
4
|
+
|
5
|
+
takes :method_stringifier
|
6
|
+
|
7
|
+
def stringify(method)
|
8
|
+
args = method_stringifier.arguments_as_string(method.parameters)
|
9
|
+
args_no_defaults = args.gsub(' = {}', '')
|
10
|
+
|
11
|
+
method_stringifier.stringify(method,
|
12
|
+
"__record__(:#{method.name}, #{args_no_defaults})")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Bogus
|
2
|
+
module MockingDSL
|
3
|
+
def fake(*args, &block)
|
4
|
+
Bogus.fake_for(*args, &block)
|
5
|
+
end
|
6
|
+
|
7
|
+
def fake_class(name, opts = {})
|
8
|
+
Bogus.fake_class(name, opts)
|
9
|
+
end
|
10
|
+
|
11
|
+
def stub(*args)
|
12
|
+
Bogus.create_stub(*args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def have_received(*args)
|
16
|
+
Bogus.have_received(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def mock(*args)
|
20
|
+
Bogus.create_mock(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def any_args
|
24
|
+
Bogus::AnyArgs
|
25
|
+
end
|
26
|
+
|
27
|
+
def anything
|
28
|
+
Bogus::Anything
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Bogus
|
2
|
+
class MultiStubber
|
3
|
+
extend Takes
|
4
|
+
takes :create_double
|
5
|
+
|
6
|
+
def stub_all(object, methods = {})
|
7
|
+
double = create_double.call(object)
|
8
|
+
methods.each do |name, result|
|
9
|
+
block = result.is_a?(Proc) ? result : proc{ result }
|
10
|
+
double.stubs(name, Bogus::AnyArgs, &block)
|
11
|
+
end
|
12
|
+
object
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Bogus
|
2
|
+
class NotAllExpectationsSatisfied < StandardError
|
3
|
+
def self.create(unsatisfied_interactions, calls)
|
4
|
+
str = <<-EOF
|
5
|
+
Some of the mocked interactions were not satisfied:
|
6
|
+
|
7
|
+
<% unsatisfied_interactions.each do |o, i| %>
|
8
|
+
- <%= render_interaction(o, i) %>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
The following calls were recorded:
|
12
|
+
|
13
|
+
<% calls.each do |o, i| %>
|
14
|
+
- <%= render_interaction(o, i) %>
|
15
|
+
<% end %>
|
16
|
+
EOF
|
17
|
+
str = str.gsub(/ {6}/, '')
|
18
|
+
template = ERB.new(str, nil, "<>")
|
19
|
+
new(template.result(binding))
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.render_interaction(object, interaction)
|
23
|
+
args = interaction.args.map(&:inspect).join(", ")
|
24
|
+
"#{object.inspect}.#{interaction.method}(#{args})"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
class Bogus::OverwritesClasses
|
2
|
-
def overwrite(
|
3
|
-
modules =
|
2
|
+
def overwrite(full_name, new_klass)
|
3
|
+
modules = full_name.split('::')
|
4
4
|
klass_name = modules.pop
|
5
5
|
parent_module = modules.reduce(Object) { |mod, name| mod.const_get(name) }
|
6
6
|
parent_module.send(:remove_const, klass_name)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Bogus
|
2
|
+
class OverwritesMethods
|
3
|
+
extend Takes
|
4
|
+
|
5
|
+
takes :makes_substitute_methods
|
6
|
+
|
7
|
+
def overwrite(object, name)
|
8
|
+
raise "wut?" if name == :__shadow__
|
9
|
+
return if object.is_a?(FakeObject)
|
10
|
+
|
11
|
+
object.extend RecordInteractions
|
12
|
+
object.extend HasOverwritenMethods
|
13
|
+
|
14
|
+
method = method_by_name(object, name)
|
15
|
+
copy = copy(object, name)
|
16
|
+
|
17
|
+
object.__overwrite__(name, method, copy)
|
18
|
+
end
|
19
|
+
|
20
|
+
def reset(object)
|
21
|
+
return if object.is_a?(FakeObject)
|
22
|
+
|
23
|
+
object.__reset__
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def method_by_name(object, name)
|
29
|
+
object.method(name) if object.methods.include?(name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def copy(object, name)
|
33
|
+
method = method_by_name(object, name)
|
34
|
+
return default_method(name) unless method
|
35
|
+
makes_substitute_methods.stringify(method)
|
36
|
+
end
|
37
|
+
|
38
|
+
def default_method(name)
|
39
|
+
"def #{name}(*args, &block); __record__(:#{name}, *args, &block); end"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Bogus
|
2
|
+
module ProxiesMethodCalls
|
3
|
+
def proxy(method_name)
|
4
|
+
MethodCallProxy.new do |name, *args, &return_value|
|
5
|
+
__send__(method_name, name, *args, &return_value)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class MethodCallProxy < BasicObject
|
11
|
+
def initialize(&on_called)
|
12
|
+
@on_called = on_called
|
13
|
+
end
|
14
|
+
|
15
|
+
def raise(*args)
|
16
|
+
::Kernel.raise(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing(name, *args, &block)
|
20
|
+
@on_called.call(name, *args, &block)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/bogus/proxy_class.rb
CHANGED
@@ -1,22 +1,31 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
module Bogus
|
2
|
+
class ProxyClass < Module
|
3
|
+
def initialize(fake_name, klass, create_recording_proxy)
|
4
|
+
@fake_name = fake_name
|
5
|
+
@klass = klass
|
6
|
+
@create_recording_proxy = create_recording_proxy
|
6
7
|
|
7
|
-
|
8
|
-
|
8
|
+
@recording_proxy = @create_recording_proxy.call(@klass, @fake_name)
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
@create_recording_proxy.call(instance, @fake_name)
|
13
|
-
end
|
11
|
+
def self.create(fake_name, klass, create_recording_proxy)
|
12
|
+
end
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
def new(*args, &block)
|
15
|
+
instance = @klass.new(*args, &block)
|
16
|
+
@create_recording_proxy.call(instance, @fake_name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing(name, *args, &block)
|
20
|
+
@recording_proxy.__send__(name, *args, &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
def const_missing(name)
|
24
|
+
@recording_proxy.__send__(:const_get, name)
|
25
|
+
end
|
18
26
|
|
19
|
-
|
20
|
-
|
27
|
+
def respond_to?(name)
|
28
|
+
@recording_proxy.respond_to?(name)
|
29
|
+
end
|
21
30
|
end
|
22
31
|
end
|
data/lib/bogus/public_methods.rb
CHANGED
@@ -1,12 +1,7 @@
|
|
1
1
|
module Bogus
|
2
2
|
module PublicMethods
|
3
|
-
|
4
|
-
|
5
|
-
inject.creates_fakes.create(*args, &block)
|
6
|
-
end
|
7
|
-
|
8
|
-
def record_calls_for(name)
|
9
|
-
inject.adds_recording.add(name)
|
3
|
+
def record_calls_for(name, klass = nil)
|
4
|
+
inject.adds_recording.add(name, klass)
|
10
5
|
end
|
11
6
|
|
12
7
|
def verify_contract!(fake_name)
|
@@ -34,14 +29,44 @@ module Bogus
|
|
34
29
|
end
|
35
30
|
|
36
31
|
def have_received(*args)
|
37
|
-
inject.
|
32
|
+
inject.have_received_matcher(*args).method_call
|
33
|
+
end
|
34
|
+
|
35
|
+
def fake_for(*args, &block)
|
36
|
+
inject.creates_fakes_with_stubbed_methods.create(*args, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
def fake_class(*args)
|
40
|
+
inject.fakes_classes.fake(*args)
|
38
41
|
end
|
39
42
|
|
40
|
-
def
|
41
|
-
|
43
|
+
def after_each_test
|
44
|
+
ensure_all_expectations_satisfied!
|
45
|
+
reset_stubbed_methods
|
46
|
+
clear_expectations
|
47
|
+
reset_overwritten_classes
|
42
48
|
end
|
43
49
|
|
44
|
-
|
50
|
+
def ensure_all_expectations_satisfied!
|
51
|
+
doubles = inject.double_tracker.doubles
|
52
|
+
inject.ensures_all_interactions_satisfied.ensure_satisfied!(doubles)
|
53
|
+
end
|
54
|
+
|
55
|
+
def clear_expectations
|
56
|
+
inject.clear_tracked_doubles
|
57
|
+
end
|
58
|
+
|
59
|
+
def reset_stubbed_methods
|
60
|
+
inject.resets_stubbed_methods.reset_all_doubles
|
61
|
+
end
|
62
|
+
|
63
|
+
def reset_overwritten_classes
|
64
|
+
inject.resets_overwritten_classes.reset
|
65
|
+
end
|
66
|
+
|
67
|
+
def fakes(&block)
|
68
|
+
inject.fake_configuration.evaluate(&block)
|
69
|
+
end
|
45
70
|
|
46
71
|
def inject
|
47
72
|
@injector ||= Bogus::Injector.new
|
@@ -1,17 +1,11 @@
|
|
1
1
|
module Bogus
|
2
2
|
module RecordInteractions
|
3
|
-
def
|
4
|
-
@
|
5
|
-
end
|
6
|
-
|
7
|
-
def __stub__
|
8
|
-
@__stub__ ||= RRProxy.stub(__inner_object__)
|
3
|
+
def __shadow__
|
4
|
+
@__shadow__ ||= Shadow.new{ self }
|
9
5
|
end
|
10
6
|
|
11
7
|
def __record__(method, *args, &block)
|
12
|
-
|
13
|
-
__inner_object__.__send__(method, *args, &block)
|
14
|
-
self
|
8
|
+
__shadow__.run(method, *args, &block)
|
15
9
|
end
|
16
10
|
end
|
17
11
|
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
class Bogus::RegistersCreatedFakes
|
2
2
|
extend Bogus::Takes
|
3
3
|
|
4
|
-
takes :creates_fakes, :fake_registry
|
4
|
+
takes :creates_fakes, :fake_registry, :double_tracker
|
5
5
|
|
6
6
|
def create(name, opts = {}, &block)
|
7
7
|
fake = creates_fakes.create(name, opts, &block)
|
8
8
|
fake_registry.store(name, fake)
|
9
|
+
double_tracker.track(fake)
|
9
10
|
fake
|
10
11
|
end
|
11
12
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Bogus
|
2
|
+
class ResetsOverwrittenClasses
|
3
|
+
extend Takes
|
4
|
+
|
5
|
+
takes :overwritten_classes, :overwrites_classes
|
6
|
+
|
7
|
+
def reset
|
8
|
+
overwritten_classes.classes.each do |name, klass|
|
9
|
+
overwrites_classes.overwrite(name, klass)
|
10
|
+
end
|
11
|
+
overwritten_classes.clear
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|