bogus 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.
Files changed (72) hide show
  1. data/.gitignore +7 -0
  2. data/.pelusa.yml +1 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +75 -0
  6. data/Guardfile +15 -0
  7. data/README.md +272 -0
  8. data/Rakefile +1 -0
  9. data/bogus.gemspec +34 -0
  10. data/features/configuration_options.feature +43 -0
  11. data/features/contract_tests_mocks.feature +100 -0
  12. data/features/contract_tests_spies.feature +72 -0
  13. data/features/contract_tests_stubs.feature +101 -0
  14. data/features/fake_objects.feature +94 -0
  15. data/features/return_value_contracts.feature +90 -0
  16. data/features/safe_mocking.feature +59 -0
  17. data/features/safe_stubbing.feature +62 -0
  18. data/features/spies.feature +78 -0
  19. data/features/step_definitions/rspec_steps.rb +68 -0
  20. data/features/support/env.rb +1 -0
  21. data/lib/bogus.rb +35 -0
  22. data/lib/bogus/adds_recording.rb +11 -0
  23. data/lib/bogus/configuration.rb +9 -0
  24. data/lib/bogus/contract_not_fulfilled.rb +24 -0
  25. data/lib/bogus/converts_name_to_class.rb +31 -0
  26. data/lib/bogus/copies_classes.rb +44 -0
  27. data/lib/bogus/creates_fakes.rb +32 -0
  28. data/lib/bogus/double.rb +10 -0
  29. data/lib/bogus/fake.rb +33 -0
  30. data/lib/bogus/fake_registry.rb +13 -0
  31. data/lib/bogus/injector.rb +64 -0
  32. data/lib/bogus/interaction.rb +24 -0
  33. data/lib/bogus/interaction_presenter.rb +29 -0
  34. data/lib/bogus/interactions_repository.rb +19 -0
  35. data/lib/bogus/invocation_matcher.rb +27 -0
  36. data/lib/bogus/method_stringifier.rb +31 -0
  37. data/lib/bogus/overwrites_classes.rb +9 -0
  38. data/lib/bogus/proxy_class.rb +22 -0
  39. data/lib/bogus/public_methods.rb +46 -0
  40. data/lib/bogus/record_interactions.rb +17 -0
  41. data/lib/bogus/recording_proxy.rb +18 -0
  42. data/lib/bogus/records_double_interactions.rb +11 -0
  43. data/lib/bogus/registers_created_fakes.rb +11 -0
  44. data/lib/bogus/rr_proxy.rb +5 -0
  45. data/lib/bogus/rspec.rb +4 -0
  46. data/lib/bogus/rspec_extensions.rb +32 -0
  47. data/lib/bogus/takes.rb +8 -0
  48. data/lib/bogus/verifies_contracts.rb +12 -0
  49. data/lib/bogus/verifies_stub_definition.rb +37 -0
  50. data/lib/bogus/version.rb +3 -0
  51. data/pelusa.sh +3 -0
  52. data/rbs.sh +3 -0
  53. data/spec/bogus/adds_recording_spec.rb +33 -0
  54. data/spec/bogus/configuration_spec.rb +17 -0
  55. data/spec/bogus/converts_name_to_class_spec.rb +40 -0
  56. data/spec/bogus/copies_classes_spec.rb +175 -0
  57. data/spec/bogus/creates_fakes_spec.rb +59 -0
  58. data/spec/bogus/double_spec.rb +31 -0
  59. data/spec/bogus/fake_registry_spec.rb +24 -0
  60. data/spec/bogus/interaction_spec.rb +68 -0
  61. data/spec/bogus/interactions_repository_spec.rb +50 -0
  62. data/spec/bogus/invocation_matcher_spec.rb +26 -0
  63. data/spec/bogus/mocking_dsl_spec.rb +76 -0
  64. data/spec/bogus/overwrites_classes_spec.rb +26 -0
  65. data/spec/bogus/proxy_class_spec.rb +95 -0
  66. data/spec/bogus/record_interactions_spec.rb +27 -0
  67. data/spec/bogus/records_double_interactions_spec.rb +25 -0
  68. data/spec/bogus/registers_created_fakes_spec.rb +25 -0
  69. data/spec/bogus/verifies_contracts_spec.rb +42 -0
  70. data/spec/bogus/verifies_stub_definition_spec.rb +71 -0
  71. data/spec/spec_helper.rb +14 -0
  72. metadata +299 -0
@@ -0,0 +1,10 @@
1
+ class Bogus::Double < BasicObject
2
+ extend ::Bogus::Takes
3
+ takes :object, :double, :verifies_stub_definition, :records_double_interactions
4
+
5
+ def method_missing(name, *args, &block)
6
+ @verifies_stub_definition.verify!(@object, name, args)
7
+ @records_double_interactions.record(@object, name, args, &block)
8
+ @double.__send__(name, *args, &block)
9
+ end
10
+ end
@@ -0,0 +1,33 @@
1
+ module Bogus
2
+ class Fake
3
+ include RecordInteractions
4
+ extend RecordInteractions
5
+
6
+ def initialize(*args)
7
+ end
8
+
9
+ def to_s
10
+ "#<#{self.class}:0x#{object_id.to_s(16)}>"
11
+ end
12
+
13
+ def kind_of?(klass)
14
+ klass == self.class.__copied_class__
15
+ end
16
+
17
+ def instance_of?(klass)
18
+ klass == self.class.__copied_class__
19
+ end
20
+
21
+ class << self
22
+ attr_accessor :__copied_class__
23
+
24
+ def name
25
+ __copied_class__.name
26
+ end
27
+
28
+ def to_s
29
+ name
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,13 @@
1
+ class Bogus::FakeRegistry
2
+ def initialize
3
+ @registry = {}
4
+ end
5
+
6
+ def store(name, object)
7
+ @registry[object.object_id] = name
8
+ end
9
+
10
+ def name(object)
11
+ @registry[object.object_id]
12
+ end
13
+ end
@@ -0,0 +1,64 @@
1
+ module Bogus
2
+ class Injector
3
+ include Dependor::AutoInject
4
+ look_in_modules Bogus
5
+
6
+ def configuration
7
+ @configuration ||= Bogus::Configuration.new
8
+ end
9
+
10
+ def search_modules
11
+ configuration.search_modules
12
+ end
13
+
14
+ def rr_proxy
15
+ Bogus::RRProxy
16
+ end
17
+
18
+ def fake_registry
19
+ @fake_registry ||= inject(Bogus::FakeRegistry)
20
+ end
21
+
22
+ def creates_fakes
23
+ creates_fakes = inject(Bogus::CreatesFakes)
24
+ inject(Bogus::RegistersCreatedFakes, creates_fakes: creates_fakes)
25
+ end
26
+
27
+ def create_stub(object)
28
+ stub = rr_proxy.stub(object)
29
+ inject(Bogus::Double, object: object, double: stub)
30
+ end
31
+
32
+ def create_mock(object)
33
+ mock = rr_proxy.mock(object)
34
+ inject(Bogus::Double, object: object, double: mock)
35
+ end
36
+
37
+ def invocation_matcher(method = nil)
38
+ inject(Bogus::InvocationMatcher, method: method)
39
+ end
40
+
41
+ def interactions_repository
42
+ raise "Specify either real_interactions or stubbed_interactions"
43
+ end
44
+
45
+ def real_interactions
46
+ @real_interactions ||= inject(Bogus::InteractionsRepository)
47
+ end
48
+
49
+ def doubled_interactions
50
+ @doubled_interactions ||= inject(Bogus::InteractionsRepository)
51
+ end
52
+
53
+ def create_proxy_class(fake_name, klass)
54
+ inject(Bogus::ProxyClass, fake_name: fake_name, klass: klass)
55
+ end
56
+
57
+ def create_recording_proxy(instance, fake_name)
58
+ inject(Bogus::RecordingProxy,
59
+ instance: instance,
60
+ fake_name: fake_name,
61
+ interactions_repository: real_interactions)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,24 @@
1
+ class Bogus::Interaction < Struct.new(:method, :args, :return_value, :error, :has_result)
2
+ def initialize(method, args, &block)
3
+ self.method = method
4
+ self.args = args
5
+
6
+ if block_given?
7
+ evaluate_return_value(block)
8
+ self.has_result = true
9
+ end
10
+ end
11
+
12
+ def ==(other)
13
+ return super(other) if has_result && other.has_result
14
+ method == other.method && args == other.args
15
+ end
16
+
17
+ private
18
+
19
+ def evaluate_return_value(block)
20
+ self.return_value = block.call
21
+ rescue => e
22
+ self.error = e.class
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ module Bogus
2
+ class InteractionPresenter
3
+ extend Bogus::Takes
4
+
5
+ takes :interaction
6
+
7
+ def to_s
8
+ "##{interaction.method}(#{args})#{result}"
9
+ end
10
+
11
+ private
12
+
13
+ def args
14
+ interaction.args.map(&:inspect).join(', ')
15
+ end
16
+
17
+ def result
18
+ error || return_value
19
+ end
20
+
21
+ def return_value
22
+ " => #{interaction.return_value.inspect}" if interaction.has_result
23
+ end
24
+
25
+ def error
26
+ " !! #{interaction.error}" if interaction.error
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ module Bogus
2
+ class InteractionsRepository
3
+ def initialize
4
+ @interactions = Hash.new { |hash, fake_name| hash[fake_name] = [] }
5
+ end
6
+
7
+ def record(fake_name, method, *args, &block)
8
+ @interactions[fake_name] << Interaction.new(method, args, &block)
9
+ end
10
+
11
+ def recorded?(fake_name, interaction)
12
+ @interactions[fake_name].include?(interaction)
13
+ end
14
+
15
+ def for_fake(fake_name)
16
+ @interactions[fake_name]
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ require 'rr'
2
+
3
+ module Bogus
4
+ class InvocationMatcher < RR::Adapters::Rspec::InvocationMatcher
5
+ def initialize(method, verifies_stub_definition, records_double_interactions)
6
+ super(method)
7
+ @verifies_stub_definition = verifies_stub_definition
8
+ @records_double_interactions = records_double_interactions
9
+ @stubbed_method_calls = []
10
+ end
11
+
12
+ def matches?(subject)
13
+ @stubbed_method_calls.each do |name, args|
14
+ @verifies_stub_definition.verify!(subject, name, args)
15
+ @records_double_interactions.record(subject, name, args)
16
+ end
17
+
18
+ return super(subject.__inner_object__) if subject.respond_to?(:__inner_object__)
19
+ return super(subject)
20
+ end
21
+
22
+ def method_missing(name, *args, &block)
23
+ @stubbed_method_calls << [name, args]
24
+ super
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ module Bogus
2
+ class MethodStringifier
3
+
4
+ def stringify(method, body)
5
+ <<-RUBY
6
+ def #{method.name}(#{arguments_as_string(method.parameters)})
7
+ #{body}
8
+ end
9
+ RUBY
10
+ end
11
+
12
+ def arguments_as_string(arguments)
13
+ arguments.map{|type, name| argument_to_string(name, type) }.compact.join(', ')
14
+ end
15
+
16
+ def argument_to_string(name, type)
17
+ if type == :req
18
+ name
19
+ elsif type == :rest
20
+ "*#{name}"
21
+ elsif type == :block
22
+ "&#{name}"
23
+ elsif type == :opt
24
+ "#{name} = {}"
25
+ else
26
+ raise "unknown argument type: #{type}"
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,9 @@
1
+ class Bogus::OverwritesClasses
2
+ def overwrite(klass, new_klass)
3
+ modules = klass.name.split('::')
4
+ klass_name = modules.pop
5
+ parent_module = modules.reduce(Object) { |mod, name| mod.const_get(name) }
6
+ parent_module.send(:remove_const, klass_name)
7
+ parent_module.const_set(klass_name, new_klass)
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ class Bogus::ProxyClass
2
+ def initialize(fake_name, klass, create_recording_proxy)
3
+ @fake_name = fake_name
4
+ @klass = klass
5
+ @create_recording_proxy = create_recording_proxy
6
+
7
+ @recording_proxy = @create_recording_proxy.call(@klass, @fake_name)
8
+ end
9
+
10
+ def new(*args, &block)
11
+ instance = @klass.new(*args, &block)
12
+ @create_recording_proxy.call(instance, @fake_name)
13
+ end
14
+
15
+ def method_missing(name, *args, &block)
16
+ @recording_proxy.__send__(name, *args, &block)
17
+ end
18
+
19
+ def respond_to?(name)
20
+ @recording_proxy.respond_to?(name)
21
+ end
22
+ end
@@ -0,0 +1,46 @@
1
+ module Bogus
2
+ module PublicMethods
3
+
4
+ def fake_for(*args, &block)
5
+ inject.creates_fakes.create(*args, &block)
6
+ end
7
+
8
+ def record_calls_for(name)
9
+ inject.adds_recording.add(name)
10
+ end
11
+
12
+ def verify_contract!(fake_name)
13
+ inject.verifies_contracts.verify(fake_name)
14
+ end
15
+
16
+ def configure(&block)
17
+ config.tap(&block)
18
+ end
19
+
20
+ def config
21
+ inject.configuration
22
+ end
23
+
24
+ def reset!
25
+ @injector = Bogus::Injector.new
26
+ end
27
+
28
+ def create_stub(object)
29
+ inject.create_stub(object)
30
+ end
31
+
32
+ def create_mock(object)
33
+ inject.create_mock(object)
34
+ end
35
+
36
+ def have_received(method = nil)
37
+ inject.invocation_matcher(method)
38
+ end
39
+
40
+ private
41
+
42
+ def inject
43
+ @injector ||= Bogus::Injector.new
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,17 @@
1
+ module Bogus
2
+ module RecordInteractions
3
+ def __inner_object__
4
+ @__inner_object__ ||= Object.new
5
+ end
6
+
7
+ def __stub__
8
+ @__stub__ ||= RRProxy.stub(__inner_object__)
9
+ end
10
+
11
+ def __record__(method, *args, &block)
12
+ __stub__.__send__(method, *args)
13
+ __inner_object__.__send__(method, *args, &block)
14
+ self
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ class Bogus::RecordingProxy < BasicObject
2
+ extend ::Bogus::Takes
3
+ takes :instance, :fake_name, :interactions_repository
4
+
5
+ def method_missing(name, *args, &block)
6
+ returned_value = @instance.__send__(name, *args, &block)
7
+ @interactions_repository.record(@fake_name, name, *args) { returned_value }
8
+ returned_value
9
+ rescue => e
10
+ @interactions_repository.record(@fake_name, name, *args) { ::Kernel.raise(e) }
11
+ ::Kernel.raise
12
+ end
13
+
14
+ def respond_to?(name)
15
+ @instance.respond_to?(name)
16
+ end
17
+ end
18
+
@@ -0,0 +1,11 @@
1
+ class Bogus::RecordsDoubleInteractions
2
+ extend Bogus::Takes
3
+
4
+ takes :doubled_interactions, :fake_registry
5
+
6
+ def record(object, method_name, args, &block)
7
+ fake_name = fake_registry.name(object)
8
+ doubled_interactions.record(fake_name, method_name, *args, &block) if fake_name
9
+ end
10
+
11
+ end
@@ -0,0 +1,11 @@
1
+ class Bogus::RegistersCreatedFakes
2
+ extend Bogus::Takes
3
+
4
+ takes :creates_fakes, :fake_registry
5
+
6
+ def create(name, opts = {}, &block)
7
+ fake = creates_fakes.create(name, opts, &block)
8
+ fake_registry.store(name, fake)
9
+ fake
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ require 'rr'
2
+
3
+ class Bogus::RRProxy
4
+ extend RR::Adapters::RRMethods
5
+ end
@@ -0,0 +1,4 @@
1
+ RSpec.configure do |config|
2
+ config.extend Bogus::RSpecExtensions
3
+ config.include Bogus::MockingDSL
4
+ end
@@ -0,0 +1,32 @@
1
+ module Bogus
2
+ module RSpecExtensions
3
+ def fake(name, opts = {}, &block)
4
+ let(name) { Bogus.fake_for(name, opts, &block) }
5
+ end
6
+
7
+ def verify_contract(name)
8
+ before do
9
+ Bogus.record_calls_for(name)
10
+ end
11
+
12
+ RSpec.configure do |config|
13
+ config.after(:suite) { Bogus.verify_contract!(name) }
14
+ end
15
+ end
16
+ end
17
+
18
+ module MockingDSL
19
+ def stub(object)
20
+ Bogus.create_stub(object)
21
+ end
22
+
23
+ def have_received(method = nil)
24
+ Bogus.have_received(method)
25
+ end
26
+
27
+ def mock(object)
28
+ Bogus.create_mock(object)
29
+ end
30
+ end
31
+ end
32
+