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,8 @@
1
+ module Bogus
2
+ module Takes
3
+ def takes(*args)
4
+ include Dependor::Constructor(*args)
5
+ attr_reader *args
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+ class Bogus::VerifiesContracts
2
+ extend Bogus::Takes
3
+
4
+ takes :doubled_interactions, :real_interactions
5
+
6
+ def verify(fake_name)
7
+ missed = doubled_interactions.for_fake(fake_name).reject do |interaction|
8
+ real_interactions.recorded?(fake_name, interaction)
9
+ end
10
+ raise Bogus::ContractNotFulfilled.new(fake_name => missed) unless missed.empty?
11
+ end
12
+ end
@@ -0,0 +1,37 @@
1
+ class Bogus::VerifiesStubDefinition
2
+ extend Bogus::Takes
3
+
4
+ takes :method_stringifier
5
+
6
+ def verify!(object, method_name, args)
7
+ stubbing_non_existent_method!(object, method_name) unless object.respond_to?(method_name)
8
+ method = object.method(method_name)
9
+ wrong_number_of_arguments!(method, args) if under_number_of_required_arguments?(method, args.size)
10
+ wrong_number_of_arguments!(method, args) if over_number_of_allowed_arguments?(method, args.size)
11
+ end
12
+
13
+ private
14
+
15
+ def wrong_number_of_arguments!(method, args)
16
+ args_string = method_stringifier.arguments_as_string(method.parameters)
17
+ raise ArgumentError, "tried to stub #{method.name}(#{args_string}) with #{args.size} arguments"
18
+ end
19
+
20
+ def stubbing_non_existent_method!(object, method_name)
21
+ raise NameError, "#{object.inspect} does not respond to #{method_name}"
22
+ end
23
+
24
+ def under_number_of_required_arguments?(method, args_count)
25
+ number_of_arguments = method.arity
26
+ number_of_arguments = -number_of_arguments - 1 if number_of_arguments < 0
27
+
28
+ args_count < number_of_arguments
29
+ end
30
+
31
+ def over_number_of_allowed_arguments?(method, args_count)
32
+ return false if method.parameters.find{|type, name| type == :rest}
33
+ number_of_arguments = method.parameters.count{|type, name| [:opt, :req].include?(type) }
34
+
35
+ args_count > number_of_arguments
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module Bogus
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ rvm rbx exec pelusa
data/rbs.sh ADDED
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ rvm 1.9.2@bogus,1.9.3@bogus,rbx19@bogus,jruby@bogus exec $@
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bogus::AddsRecording do
4
+ module SampleModule
5
+ class Library
6
+ end
7
+ end
8
+
9
+ let(:converts_name_to_class) { stub }
10
+ let(:create_proxy_class) { stub }
11
+ let(:overwrites_classes) { stub }
12
+ let(:adds_recording) { Bogus::AddsRecording.new(converts_name_to_class, create_proxy_class, overwrites_classes) }
13
+
14
+ before do
15
+ stub(converts_name_to_class).convert { SampleModule::Library }
16
+ stub(create_proxy_class).call { Object }
17
+ stub(overwrites_classes).overwrite
18
+
19
+ adds_recording.add(:library)
20
+ end
21
+
22
+ it "converts the fake name to class" do
23
+ converts_name_to_class.should have_received.convert(:library)
24
+ end
25
+
26
+ it "creates the proxy" do
27
+ create_proxy_class.should have_received.call(:library, SampleModule::Library)
28
+ end
29
+
30
+ it "swaps the classes" do
31
+ overwrites_classes.should have_received.overwrite(SampleModule::Library, Object)
32
+ end
33
+ end
@@ -0,0 +1,17 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Bogus::Configuration do
4
+ after { Bogus.reset! }
5
+
6
+ it "stores the modules to be searched" do
7
+ Bogus.configure do |c|
8
+ c.search_modules << String
9
+ end
10
+
11
+ Bogus.config.search_modules.should == [Object, String]
12
+ end
13
+
14
+ it "defaults search_modules to [Object]" do
15
+ Bogus.config.search_modules.should == [Object]
16
+ end
17
+ end
@@ -0,0 +1,40 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Bogus::ConvertsNameToClass do
4
+ FooBarBaz = Class.new
5
+
6
+ module Foo
7
+ FooBarBaz = Class.new
8
+ end
9
+
10
+ module Bar
11
+ FooBarBaz = Class.new
12
+ Bam = Class.new
13
+ end
14
+
15
+ it "finds classes in golbal namespace by default" do
16
+ converts_name_to_class = Bogus::ConvertsNameToClass.new(Bogus.config.search_modules)
17
+
18
+ converts_name_to_class.convert(:foo_bar_baz).should == FooBarBaz
19
+ end
20
+
21
+ it "looks in the modules in the specified order" do
22
+ converts_name_to_class = Bogus::ConvertsNameToClass.new([Foo, Bar])
23
+
24
+ converts_name_to_class.convert(:foo_bar_baz).should == Foo::FooBarBaz
25
+ end
26
+
27
+ it "looks in the next module on the list if the first does not contain the class" do
28
+ converts_name_to_class = Bogus::ConvertsNameToClass.new([Foo, Bar])
29
+
30
+ converts_name_to_class.convert(:bam).should == Bar::Bam
31
+ end
32
+
33
+ it "raises an error if it can't find the class" do
34
+ converts_name_to_class = Bogus::ConvertsNameToClass.new([Foo])
35
+
36
+ expect do
37
+ converts_name_to_class.convert(:bam)
38
+ end.to raise_error(Bogus::ConvertsNameToClass::CanNotFindClass)
39
+ end
40
+ end
@@ -0,0 +1,175 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Bogus::CopiesClasses do
4
+ module SampleMethods
5
+ def foo
6
+ end
7
+
8
+ def bar(x)
9
+ end
10
+
11
+ def baz(x, *y)
12
+ end
13
+
14
+ def bam(opts = {})
15
+ end
16
+
17
+ def baa(x, &block)
18
+ end
19
+ end
20
+
21
+ shared_examples_for 'the copied class' do
22
+ it "copies methods with no arguments" do
23
+ subject.should respond_to(:foo)
24
+ subject.foo
25
+ end
26
+
27
+ it "copies methods with explicit arguments" do
28
+ subject.should respond_to(:bar)
29
+
30
+ subject.method(:bar).arity.should == 1
31
+
32
+ subject.bar('hello')
33
+ end
34
+
35
+ it "copies methods with variable arguments" do
36
+ subject.should respond_to(:baz)
37
+
38
+ subject.baz('hello', 'foo', 'bar', 'baz')
39
+ end
40
+
41
+ it "copies methods with default arguments" do
42
+ subject.should respond_to(:bam)
43
+
44
+ subject.bam
45
+ subject.bam(hello: 'world')
46
+ end
47
+
48
+ it "copies methods with block arguments" do
49
+ subject.should respond_to(:baa)
50
+
51
+ subject.baa('hello')
52
+ subject.baa('hello') {}
53
+ end
54
+
55
+ it "makes the methods chainable" do
56
+ subject.foo.bar('hello').baz('hello', 'world', 'foo').bam.baa('foo')
57
+ end
58
+ end
59
+
60
+ let(:method_stringifier) { Bogus::MethodStringifier.new }
61
+ let(:copies_classes) { Bogus::CopiesClasses.new(method_stringifier) }
62
+ let(:fake_class) { copies_classes.copy(klass) }
63
+ let(:fake) { fake_class.new }
64
+
65
+ class FooWithInstanceMethods
66
+ include SampleMethods
67
+ end
68
+
69
+ context "instance methods" do
70
+ let(:klass) { FooWithInstanceMethods }
71
+ subject{ fake }
72
+
73
+ it_behaves_like 'the copied class'
74
+ end
75
+
76
+ context "constructors" do
77
+ let(:klass) {
78
+ Class.new do
79
+ def initialize(hello)
80
+ end
81
+ end
82
+ }
83
+
84
+ it "adds a constructor that allows passing any number of arguments" do
85
+ fake_class.new('hello', 'w', 'o', 'r', 'l', 'd') { test }
86
+ end
87
+ end
88
+
89
+ class ClassWithClassMethods
90
+ extend SampleMethods
91
+ end
92
+
93
+ context "class methods" do
94
+ let(:klass) { ClassWithClassMethods }
95
+ subject{ fake_class }
96
+
97
+ it_behaves_like 'the copied class'
98
+ end
99
+
100
+ context "identification" do
101
+ module SomeModule
102
+ class SomeClass
103
+ end
104
+ end
105
+
106
+ let(:klass) { SomeModule::SomeClass }
107
+
108
+ it "should copy the class name" do
109
+ fake.class.name.should == 'SomeModule::SomeClass'
110
+ end
111
+
112
+ it "should override kind_of?" do
113
+ fake.should be_kind_of(SomeModule::SomeClass)
114
+ end
115
+
116
+ it "should override instance_of?" do
117
+ fake.should be_instance_of(SomeModule::SomeClass)
118
+ end
119
+
120
+ it "should override is_a?" do
121
+ fake.should be_a(SomeModule::SomeClass)
122
+ end
123
+
124
+ it "should include class name in the output of fake's class #to_s" do
125
+ fake.class.to_s.should include(klass.name)
126
+ end
127
+
128
+ it "should include class name in the output of fake's #to_s" do
129
+ fake.to_s.should include(klass.name)
130
+ end
131
+
132
+ # TODO
133
+ it "should override kind_of?/instance_of? for base classes of copied class"
134
+ it "should override kind_of? for modules included into copied class"
135
+ end
136
+
137
+ shared_examples_for 'spying' do
138
+ def should_record(method, *args)
139
+ mock(subject).__record__(method, *args)
140
+
141
+ subject.send(method, *args)
142
+ end
143
+
144
+ it "records method calls with no arguments" do
145
+ should_record(:foo)
146
+ end
147
+
148
+ it "records method calls with explicit arguments" do
149
+ should_record(:bar, 'hello')
150
+ end
151
+
152
+ it "records method calls with variable arguments" do
153
+ should_record(:baz, 'hello', 'foo', 'bar', 'baz')
154
+ end
155
+
156
+ it "records method calls with default arguments" do
157
+ should_record(:bam, hello: 'world')
158
+ end
159
+ end
160
+
161
+ context "spying on an instance" do
162
+ let(:klass) { FooWithInstanceMethods }
163
+ subject{ fake }
164
+
165
+ include_examples 'spying'
166
+ end
167
+
168
+ context "spying on copied class" do
169
+ let(:klass) { ClassWithClassMethods }
170
+ subject { fake_class }
171
+
172
+ include_examples 'spying'
173
+ end
174
+ end
175
+
@@ -0,0 +1,59 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Bogus::CreatesFakes do
4
+ let(:fake_class) { stub() }
5
+ let(:fake_instance) { stub() }
6
+ let(:converts_name_to_class) { stub() }
7
+ let(:copies_classes) { stub() }
8
+ let(:creates_fakes) { Bogus::CreatesFakes.new(copies_classes, converts_name_to_class) }
9
+
10
+ module Foo
11
+ end
12
+
13
+ module Bar
14
+ end
15
+
16
+ before { stub(fake_class).new{fake_instance} }
17
+
18
+ context "without block" do
19
+ before do
20
+ mock(converts_name_to_class).convert(:foo) { Foo }
21
+ mock(copies_classes).copy(Foo) { fake_class }
22
+ end
23
+
24
+ it "creates a new instance of copied class by default" do
25
+ creates_fakes.create(:foo).should == fake_instance
26
+ end
27
+
28
+ it "creates a new instance of copied class if called with as: :instance" do
29
+ creates_fakes.create(:foo, as: :instance).should == fake_instance
30
+ end
31
+
32
+ it "copies class but does not create an instance if called with as: :class" do
33
+ creates_fakes.create(:foo, as: :class).should == fake_class
34
+ end
35
+
36
+ it "raises an error if the as mode is not known" do
37
+ expect do
38
+ creates_fakes.create(:foo, as: :something)
39
+ end.to raise_error(Bogus::CreatesFakes::UnknownMode)
40
+ end
41
+ end
42
+
43
+ context "with block" do
44
+ before do
45
+ stub(converts_name_to_class).convert
46
+ mock(copies_classes).copy(Bar) { fake_class }
47
+ end
48
+
49
+ it "uses the class provided" do
50
+ creates_fakes.create(:foo){Bar}.should == fake_instance
51
+ end
52
+
53
+ it "does not convert the class name" do
54
+ creates_fakes.create(:foo) { Bar}
55
+
56
+ copies_classes.should_not have_received.convert
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bogus::Double do
4
+ let(:rr_double) { stub }
5
+ let(:verifies_stub_definition) { stub }
6
+ let(:records_double_interactions) { stub }
7
+
8
+ let(:object) { "strings have plenty of methods to call" }
9
+
10
+ let(:bogus_double) { isolate(Bogus::Double, double: rr_double) }
11
+
12
+ before do
13
+ stub(verifies_stub_definition).verify!
14
+ stub(records_double_interactions).record
15
+ stub(rr_double).method_name
16
+
17
+ bogus_double.method_name(:foo, :bar)
18
+ end
19
+
20
+ it "verifies that stub definition matches the real definition" do
21
+ verifies_stub_definition.should have_received.verify!(object, :method_name, [:foo, :bar])
22
+ end
23
+
24
+ it "records the stub interaction so that it can be verified later" do
25
+ records_double_interactions.should have_received.record(object, :method_name, [:foo, :bar])
26
+ end
27
+
28
+ it "proxies the method call" do
29
+ rr_double.should have_received.method_name(:foo, :bar)
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bogus::FakeRegistry do
4
+ let(:fake_registry) { Bogus::FakeRegistry.new }
5
+
6
+ it "knows the fake's names" do
7
+ object = Object.new
8
+
9
+ fake_registry.store(:name, object)
10
+
11
+ fake_registry.name(object).should == :name
12
+ end
13
+
14
+ it "returns name based on object identity" do
15
+ example = Struct.new(:id)
16
+
17
+ object = example.new(1)
18
+ duplicate = example.new(1)
19
+
20
+ fake_registry.store(:object, object)
21
+
22
+ fake_registry.name(duplicate).should be_nil
23
+ end
24
+ end