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,68 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bogus::Interaction do
4
+ class SomeError < StandardError; end
5
+
6
+ same = [
7
+ [[:foo, [:bar], "value"], [:foo, [:bar], "value"]],
8
+ [[:foo, [:bar]], [:foo, [:bar], "value"]],
9
+ [[:foo, [:bar], "value"], [:foo, [:bar]]],
10
+ [[:foo, [:bar]], [:foo, [:bar]]]
11
+ ]
12
+
13
+ different = [
14
+ [[:foo, [:bar], "value"], [:foo, [:bar], "value2"]],
15
+ [[:foo, [:bar], "value"], [:baz, [:bar], "value"]],
16
+ [[:foo, [:baz], "value"], [:foo, [:bar], "value"]],
17
+ [[:foo, [:bar]], [:foo, [:baz]]],
18
+ [[:baz, [:bar]], [:foo, [:bar]]]
19
+ ]
20
+
21
+ def create_interaction(interaction)
22
+ method_name, args, return_value = interaction
23
+ if return_value
24
+ Bogus::Interaction.new(method_name, args) { return_value }
25
+ else
26
+ Bogus::Interaction.new(method_name, args)
27
+ end
28
+ end
29
+
30
+ same.each do |first_interaction, second_interaction|
31
+ it "returns true for #{first_interaction.inspect} and #{second_interaction.inspect}" do
32
+ first = create_interaction(first_interaction)
33
+ second = create_interaction(second_interaction)
34
+
35
+ first.should == second
36
+ end
37
+ end
38
+
39
+ different.each do |first_interaction, second_interaction|
40
+ it "returns false for #{first_interaction.inspect} and #{second_interaction.inspect}" do
41
+ first = create_interaction(first_interaction)
42
+ second = create_interaction(second_interaction)
43
+
44
+ first.should_not == second
45
+ end
46
+ end
47
+
48
+ it "differs exceptions from empty return values" do
49
+ first = Bogus::Interaction.new(:foo, :bar) { raise SomeError }
50
+ second = Bogus::Interaction.new(:foo, :bar) { nil }
51
+
52
+ first.should_not == second
53
+ end
54
+
55
+ it "differs raised exceptions from ones just returned from the block" do
56
+ first = Bogus::Interaction.new(:foo, :bar) { raise SomeError }
57
+ second = Bogus::Interaction.new(:foo, :bar) { SomeError }
58
+
59
+ first.should_not == second
60
+ end
61
+
62
+ it "considers exceptions of the same type as equal" do
63
+ first = Bogus::Interaction.new(:foo, :bar) { raise SomeError }
64
+ second = Bogus::Interaction.new(:foo, :bar) { raise SomeError }
65
+
66
+ first.should == second
67
+ end
68
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bogus::InteractionsRepository do
4
+ let(:interactions_repository) { Bogus::InteractionsRepository.new }
5
+
6
+ def recorded?(fake_name, method, *args)
7
+ interactions_repository.recorded?(fake_name, Bogus::Interaction.new(method, args))
8
+ end
9
+
10
+ it "considers the interaction recorded if it was recorded previously" do
11
+ interactions_repository.record(:foo, :bar, 1, 2, 3)
12
+
13
+ recorded?(:foo, :bar, 1, 2, 3).should be_true
14
+ end
15
+
16
+ it "considers the interaction recorded if it returned the same value as passed block" do
17
+ interactions_repository.record(:foo, :bar) { "a result" }
18
+ interaction = Bogus::Interaction.new(:bar, []) { "a result" }
19
+
20
+ interactions_repository.recorded?(:foo, interaction).should be_true
21
+ end
22
+
23
+ it "does not consider any interactions recorded prior to any recordings" do
24
+ recorded?(:foo, :bar, 1).should be_false
25
+ end
26
+
27
+ it "does not consider the interaction recorded with a different fake name" do
28
+ interactions_repository.record(:baz, :bar, 1)
29
+
30
+ recorded?(:foo, :bar, 1).should be_false
31
+ end
32
+
33
+ it "does not consider the interaction recorded with a different method name" do
34
+ interactions_repository.record(:foo, :baz, 1)
35
+
36
+ recorded?(:foo, :bar, 1).should be_false
37
+ end
38
+
39
+ it "does not consider the interaction recorded with different method arguments" do
40
+ interactions_repository.record(:foo, :bar, 1, 2)
41
+
42
+ recorded?(:foo, :bar, 1).should be_false
43
+ end
44
+
45
+ it "returns a list of interactions for given fake" do
46
+ interactions_repository.record(:foo, :bar, 1, 2)
47
+
48
+ interactions_repository.for_fake(:foo).should == [Bogus::Interaction.new(:bar, [1, 2])]
49
+ end
50
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bogus::InvocationMatcher do
4
+ let(:object) { stub }
5
+ let(:method) { nil }
6
+ let(:records_double_interactions) { stub }
7
+ let(:verifies_stub_definition) { stub }
8
+
9
+ let(:invocation_matcher) { isolate(Bogus::InvocationMatcher) }
10
+
11
+ before do
12
+ stub(verifies_stub_definition).verify!
13
+ stub(records_double_interactions).record
14
+
15
+ invocation_matcher.the_method(:foo, :bar)
16
+ invocation_matcher.matches?(object)
17
+ end
18
+
19
+ it "verifies stub definition" do
20
+ verifies_stub_definition.should have_received.verify!(object, :the_method, [:foo, :bar])
21
+ end
22
+
23
+ it "records double interacions" do
24
+ records_double_interactions.should have_received.record(object, :the_method, [:foo, :bar])
25
+ end
26
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bogus::MockingDSL do
4
+ class ExampleFoo
5
+ def foo(bar)
6
+ end
7
+ end
8
+
9
+ class Stubber
10
+ extend Bogus::MockingDSL
11
+ end
12
+
13
+ describe "#stub" do
14
+ it "allows stubbing the existing methods" do
15
+ baz = ExampleFoo.new
16
+
17
+ Stubber.stub(baz).foo("bar") { :return_value }
18
+
19
+ baz.foo("bar").should == :return_value
20
+ end
21
+
22
+ it "does not allow stubbing non-existent methods" do
23
+ baz = ExampleFoo.new
24
+ expect do
25
+ Stubber.stub(baz).does_not_exist("bar") { :return_value }
26
+ end.to raise_error(NameError)
27
+ end
28
+ end
29
+
30
+ describe "#have_received" do
31
+ context "with a fake object" do
32
+ let(:the_fake) { Bogus.fake_for(:example_foo) }
33
+
34
+ it "allows verifying that fakes have correct interfaces" do
35
+ the_fake.foo("test")
36
+
37
+ the_fake.should Stubber.have_received.foo("test")
38
+ end
39
+
40
+ it "does not allow verifying on non-existent methods" do
41
+ expect {
42
+ the_fake.should Stubber.have_received.bar("test")
43
+ }.to raise_error(NameError)
44
+ end
45
+
46
+ it "does not allow verifying on methods with a wrong argument count" do
47
+ expect {
48
+ the_fake.should Stubber.have_received.foo("test", "test 2")
49
+ }.to raise_error(ArgumentError)
50
+ end
51
+ end
52
+
53
+ it "can be used with plain old Ruby objects" do
54
+ object = ExampleFoo.new
55
+ stub(object).foo
56
+
57
+ object.foo('test')
58
+
59
+ object.should Stubber.have_received.foo("test")
60
+ end
61
+ end
62
+
63
+ class Mocker
64
+ extend Bogus::MockingDSL
65
+ end
66
+
67
+ describe "#mock" do
68
+ it "allows mocking the existing methods" do
69
+ baz = ExampleFoo.new
70
+
71
+ Mocker.mock(baz).foo("bar") { :return_value }
72
+
73
+ baz.foo("bar").should == :return_value
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bogus::OverwritesClasses do
4
+ module SampleOuterModule
5
+ module SampleModule
6
+ class SampleClass
7
+ end
8
+ end
9
+ end
10
+
11
+ let(:new_class) { Class.new }
12
+ let(:overwrites_classes) { Bogus::OverwritesClasses.new }
13
+
14
+ it "overwrites nested classes" do
15
+ overwrites_classes.overwrite(SampleOuterModule::SampleModule::SampleClass, new_class)
16
+
17
+ SampleOuterModule::SampleModule::SampleClass.should equal(new_class)
18
+ end
19
+
20
+ it "overwrites top level classes" do
21
+ overwrites_classes.overwrite(SampleOuterModule, new_class)
22
+
23
+ SampleOuterModule.should equal(new_class)
24
+ end
25
+ end
26
+
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bogus::ProxyClass do
4
+ module SampleModule
5
+ class GrandLibrary
6
+ def checkout(book, user)
7
+ :checkouted
8
+ end
9
+
10
+ def self.find_by_address(address)
11
+ :the_library
12
+ end
13
+
14
+ def self.find_by_isbn(isbn)
15
+ raise StandardError
16
+ end
17
+ end
18
+ end
19
+
20
+ let(:proxy_class) { Bogus::ProxyClass.new(:fake_name, SampleModule::GrandLibrary, create_recording_proxy) }
21
+
22
+ let(:create_recording_proxy) do
23
+ lambda {|instance, fake_name| Bogus::RecordingProxy.new(instance, fake_name, interactions_repository) }
24
+ end
25
+
26
+ let(:interactions_repository) { FakeRepository.new }
27
+
28
+ it "returns the proxy" do
29
+ proxy_class.new.checkout("Moby Dick", "Bob").should == :checkouted
30
+ end
31
+
32
+ it "records interactions with created objects" do
33
+ proxy_class.new.checkout("Moby Dick", "Bob")
34
+
35
+ interactions_repository.should have_recorded(:fake_name, :checkout, "Moby Dick", "Bob")
36
+ end
37
+
38
+ it "responds to every method that the original class responds to" do
39
+ proxy_class.should respond_to(:find_by_address)
40
+ end
41
+
42
+ it "delegates interactions with the proxy class to wrapped class" do
43
+ proxy_class.find_by_address("some address").should == :the_library
44
+ end
45
+
46
+ it "records interactions with the proxy class" do
47
+ proxy_class.find_by_address("some address")
48
+
49
+ interactions_repository.should have_recorded(:fake_name, :find_by_address, "some address")
50
+ end
51
+
52
+ it "records return values" do
53
+ proxy_class.find_by_address("some address")
54
+
55
+ interactions_repository.return_value(:fake_name, :find_by_address, "some address").should == :the_library
56
+ end
57
+
58
+ it "re-raises exceptions" do
59
+ expect {
60
+ proxy_class.find_by_isbn("some isbn")
61
+ }.to raise_error(StandardError)
62
+ end
63
+
64
+ it "records raised exceptions" do
65
+ proxy_class.find_by_isbn("some isbn") rescue nil
66
+
67
+ expect {
68
+ interactions_repository.return_value(:fake_name, :find_by_isbn, "some isbn")
69
+ }.to raise_error(StandardError)
70
+ end
71
+
72
+ class FakeRepository
73
+ def initialize
74
+ @recordings = []
75
+ end
76
+
77
+ def record(fake_name, method, *args, &block)
78
+ @recordings << [fake_name, method, args, block]
79
+ end
80
+
81
+ def has_recorded?(fake_name, method, *args)
82
+ !!find(fake_name, method, *args)
83
+ end
84
+
85
+ def return_value(fake_name, method, *args)
86
+ find(fake_name, method, *args).last.call
87
+ end
88
+
89
+ def find(fake_name, method, *args)
90
+ @recordings.find do |f, m, a, b|
91
+ [f, m, a] == [fake_name, method, args]
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bogus::RecordInteractions do
4
+
5
+ class SampleRecordsInteractions
6
+ include Bogus::RecordInteractions
7
+ end
8
+
9
+ let(:sample) { SampleRecordsInteractions.new }
10
+ let!(:rr) { Bogus::RRProxy }
11
+
12
+ it "allows verifying that interactions happened" do
13
+ sample.__record__(:foo, 1, 2, 3)
14
+
15
+ sample.__inner_object__.should have_received.foo(1, 2, 3)
16
+ end
17
+
18
+ it "allows verifying that interactions didn't happen" do
19
+ sample.__record__(:bar)
20
+
21
+ sample.__inner_object__.should_not have_received.foo
22
+ end
23
+
24
+ it "returns self from record" do
25
+ sample.__record__(:foo).should == sample
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bogus::RecordsDoubleInteractions do
4
+ let(:fake_registry) { stub }
5
+ let(:doubled_interactions) { stub }
6
+ let(:object) { Object.new }
7
+
8
+ let(:records_double_interactions) { isolate(Bogus::RecordsDoubleInteractions) }
9
+
10
+ it "records the call in double interaction repository" do
11
+ stub(fake_registry).name(object) { :object_name }
12
+ stub(doubled_interactions).record
13
+
14
+ records_double_interactions.record(object, :method_name, [:foo, 1])
15
+
16
+ doubled_interactions.should have_received.record(:object_name, :method_name, :foo, 1)
17
+ end
18
+
19
+ it "does not record the interaction if object is not a fake" do
20
+ stub(fake_registry).name(object) { nil }
21
+ dont_allow(doubled_interactions).record
22
+
23
+ records_double_interactions.record(object, :method_name, [:foo, 1])
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bogus::RegistersCreatedFakes do
4
+ let(:fake_registry) { stub }
5
+ let(:creates_fakes) { stub }
6
+
7
+ let(:registers_created_fakes) { isolate(Bogus::RegistersCreatedFakes) }
8
+
9
+ before do
10
+ stub(fake_registry).store
11
+ stub(creates_fakes).create { :the_fake }
12
+ end
13
+
14
+ it "registers the fakes created by creates_fakes" do
15
+ registers_created_fakes.create(:foo, as: :instance) { Object }
16
+
17
+ fake_registry.should have_received.store(:foo, :the_fake)
18
+ end
19
+
20
+ it "returns the created fake" do
21
+ fake = registers_created_fakes.create(:foo, as: :instance) { Object }
22
+
23
+ fake.should == :the_fake
24
+ end
25
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bogus::VerifiesContracts do
4
+ let(:real_interactions) { stub }
5
+ let(:doubled_interactions) { stub }
6
+ let(:verifies_contracts) { isolate(Bogus::VerifiesContracts) }
7
+
8
+ let(:matched_interaction) { interaction("matched") }
9
+
10
+ it "fails unmatched calls" do
11
+ first_interaction = interaction("first")
12
+ second_interaction = interaction("second")
13
+
14
+ stub(doubled_interactions).for_fake(:fake_name){[first_interaction, matched_interaction, second_interaction]}
15
+
16
+ stub(real_interactions).recorded?(:fake_name, first_interaction) { false }
17
+ stub(real_interactions).recorded?(:fake_name, second_interaction) { false }
18
+ stub(real_interactions).recorded?(:fake_name, matched_interaction) { true }
19
+
20
+ expect_verify_to_raise_error_with_interactions(:fake_name, [first_interaction, second_interaction])
21
+ end
22
+
23
+ it "passes with all calls matched" do
24
+ stub(doubled_interactions).for_fake(:fake_name) { [matched_interaction] }
25
+ stub(real_interactions).recorded?(:fake_name, matched_interaction) { true }
26
+
27
+ expect {
28
+ verifies_contracts.verify(:fake_name)
29
+ }.not_to raise_error
30
+ end
31
+
32
+ def expect_verify_to_raise_error_with_interactions(name, interactions)
33
+ verifies_contracts.verify(name)
34
+ fail
35
+ rescue Bogus::ContractNotFulfilled => contract_error
36
+ contract_error.interactions.should == { name => interactions }
37
+ end
38
+
39
+ def interaction(method)
40
+ Bogus::Interaction.new(method, [:foo, :bar]) { "return value" }
41
+ end
42
+ end