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.
- data/.gitignore +7 -0
- data/.pelusa.yml +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +75 -0
- data/Guardfile +15 -0
- data/README.md +272 -0
- data/Rakefile +1 -0
- data/bogus.gemspec +34 -0
- data/features/configuration_options.feature +43 -0
- data/features/contract_tests_mocks.feature +100 -0
- data/features/contract_tests_spies.feature +72 -0
- data/features/contract_tests_stubs.feature +101 -0
- data/features/fake_objects.feature +94 -0
- data/features/return_value_contracts.feature +90 -0
- data/features/safe_mocking.feature +59 -0
- data/features/safe_stubbing.feature +62 -0
- data/features/spies.feature +78 -0
- data/features/step_definitions/rspec_steps.rb +68 -0
- data/features/support/env.rb +1 -0
- data/lib/bogus.rb +35 -0
- data/lib/bogus/adds_recording.rb +11 -0
- data/lib/bogus/configuration.rb +9 -0
- data/lib/bogus/contract_not_fulfilled.rb +24 -0
- data/lib/bogus/converts_name_to_class.rb +31 -0
- data/lib/bogus/copies_classes.rb +44 -0
- data/lib/bogus/creates_fakes.rb +32 -0
- data/lib/bogus/double.rb +10 -0
- data/lib/bogus/fake.rb +33 -0
- data/lib/bogus/fake_registry.rb +13 -0
- data/lib/bogus/injector.rb +64 -0
- data/lib/bogus/interaction.rb +24 -0
- data/lib/bogus/interaction_presenter.rb +29 -0
- data/lib/bogus/interactions_repository.rb +19 -0
- data/lib/bogus/invocation_matcher.rb +27 -0
- data/lib/bogus/method_stringifier.rb +31 -0
- data/lib/bogus/overwrites_classes.rb +9 -0
- data/lib/bogus/proxy_class.rb +22 -0
- data/lib/bogus/public_methods.rb +46 -0
- data/lib/bogus/record_interactions.rb +17 -0
- data/lib/bogus/recording_proxy.rb +18 -0
- data/lib/bogus/records_double_interactions.rb +11 -0
- data/lib/bogus/registers_created_fakes.rb +11 -0
- data/lib/bogus/rr_proxy.rb +5 -0
- data/lib/bogus/rspec.rb +4 -0
- data/lib/bogus/rspec_extensions.rb +32 -0
- data/lib/bogus/takes.rb +8 -0
- data/lib/bogus/verifies_contracts.rb +12 -0
- data/lib/bogus/verifies_stub_definition.rb +37 -0
- data/lib/bogus/version.rb +3 -0
- data/pelusa.sh +3 -0
- data/rbs.sh +3 -0
- data/spec/bogus/adds_recording_spec.rb +33 -0
- data/spec/bogus/configuration_spec.rb +17 -0
- data/spec/bogus/converts_name_to_class_spec.rb +40 -0
- data/spec/bogus/copies_classes_spec.rb +175 -0
- data/spec/bogus/creates_fakes_spec.rb +59 -0
- data/spec/bogus/double_spec.rb +31 -0
- data/spec/bogus/fake_registry_spec.rb +24 -0
- data/spec/bogus/interaction_spec.rb +68 -0
- data/spec/bogus/interactions_repository_spec.rb +50 -0
- data/spec/bogus/invocation_matcher_spec.rb +26 -0
- data/spec/bogus/mocking_dsl_spec.rb +76 -0
- data/spec/bogus/overwrites_classes_spec.rb +26 -0
- data/spec/bogus/proxy_class_spec.rb +95 -0
- data/spec/bogus/record_interactions_spec.rb +27 -0
- data/spec/bogus/records_double_interactions_spec.rb +25 -0
- data/spec/bogus/registers_created_fakes_spec.rb +25 -0
- data/spec/bogus/verifies_contracts_spec.rb +42 -0
- data/spec/bogus/verifies_stub_definition_spec.rb +71 -0
- data/spec/spec_helper.rb +14 -0
- metadata +299 -0
data/lib/bogus/takes.rb
ADDED
@@ -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
|
data/pelusa.sh
ADDED
data/rbs.sh
ADDED
@@ -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
|