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,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