bogus 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+