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,59 @@
1
+ Feature: Safe mocking
2
+
3
+ Background:
4
+ Given a file named "foo.rb" with:
5
+ """ruby
6
+ class Library
7
+ def checkout(book)
8
+ end
9
+ end
10
+ """
11
+
12
+ Scenario: Mocking methods that exist on real object
13
+ Then spec file with following content should pass:
14
+ """ruby
15
+ describe Library do
16
+
17
+ it "does something" do
18
+ library = Library.new
19
+ mock(library).checkout("some book") { :checked_out }
20
+
21
+ library.checkout("some book").should == :checked_out
22
+ end
23
+ end
24
+ """
25
+
26
+ Scenario: Mocking methods that do not exist on real object
27
+ Then spec file with following content should fail:
28
+ """ruby
29
+ describe Library do
30
+ it "does something" do
31
+ library = Library.new
32
+ mock(library).buy("some book") { :bought }
33
+ library.buy("some book")
34
+ end
35
+ end
36
+ """
37
+
38
+ Scenario: Mocking methods with wrong number of arguments
39
+ Then spec file with following content should fail:
40
+ """ruby
41
+ describe Library do
42
+ it "does something" do
43
+ library = Library.new
44
+ mock(library).checkout("some book", "another book") { :bought }
45
+ library.checkout("some book", "another book")
46
+ end
47
+ end
48
+ """
49
+
50
+ Scenario: Mocks require the methods to be called
51
+ Then spec file with following content should fail:
52
+ """ruby
53
+ describe Library do
54
+ it "does something" do
55
+ library = Library.new
56
+ mock(library).checkout("some book") { :bought }
57
+ end
58
+ end
59
+ """
@@ -0,0 +1,62 @@
1
+ Feature: Safe stubbing
2
+
3
+ Most Ruby test double libraries let you stub methods that don't exist.
4
+ Bogus is different in this respect: not only does it not allow stubbing
5
+ methods that don't exist, it also ensures that the number of arguments
6
+ you pass to those methods matches the method definition.
7
+
8
+ Background:
9
+ Given a file named "foo.rb" with:
10
+ """ruby
11
+ class Library
12
+ def checkout(book)
13
+ end
14
+ end
15
+ """
16
+
17
+ Scenario: Stubbing methods that exist on real object
18
+ Then spec file with following content should pass:
19
+ """ruby
20
+ describe Library do
21
+
22
+ it "does something" do
23
+ library = Library.new
24
+ stub(library).checkout("some book") { :checked_out }
25
+
26
+ library.checkout("some book").should == :checked_out
27
+ end
28
+ end
29
+ """
30
+
31
+ Scenario: Stubbing methods that do not exist on real object
32
+ Then spec file with following content should fail:
33
+ """ruby
34
+ describe Library do
35
+ it "does something" do
36
+ library = Library.new
37
+ stub(library).buy("some book") { :bought }
38
+ end
39
+ end
40
+ """
41
+
42
+ Scenario: Stubbing methods with wrong number of arguments
43
+ Then spec file with following content should fail:
44
+ """ruby
45
+ describe Library do
46
+ it "does something" do
47
+ library = Library.new
48
+ stub(library).checkout("some book", "another book") { :bought }
49
+ end
50
+ end
51
+ """
52
+
53
+ Scenario: Stubs allow the methods to be called
54
+ Then spec file with following content should pass:
55
+ """ruby
56
+ describe Library do
57
+ it "does something" do
58
+ library = Library.new
59
+ stub(library).checkout("some book") { :bought }
60
+ end
61
+ end
62
+ """
@@ -0,0 +1,78 @@
1
+ Feature: Spies
2
+
3
+ Object Oriented Programming is all about messages sent between the objects.
4
+ If you follow principles like "Tell, Don't Ask", you will enable yourself
5
+ to combine Bogus's powerful feature of faking objects with it's ability
6
+ to verify object interactions.
7
+
8
+ Background:
9
+ Given a file named "foo.rb" with:
10
+ """ruby
11
+ class Library
12
+ def checkout(book)
13
+ # marks book as checked out
14
+ end
15
+ end
16
+
17
+ class Student
18
+ def initialize(library)
19
+ @library = library
20
+ end
21
+
22
+ def study(*book_titles)
23
+ book_titles.each do |book_title|
24
+ @library.checkout(book_title)
25
+ end
26
+ end
27
+ end
28
+ """
29
+
30
+ Scenario: Ensuring methods were called
31
+ Then spec file with following content should pass:
32
+ """ruby
33
+ describe Student do
34
+ fake(:library)
35
+
36
+ it "studies using books from library" do
37
+ student = Student.new(library)
38
+
39
+ student.study("Moby Dick", "Sherlock Holmes")
40
+
41
+ library.should have_received.checkout("Moby Dick")
42
+ library.should have_received.checkout("Sherlock Holmes")
43
+ end
44
+ end
45
+ """
46
+
47
+ Scenario: Spying on methods that do not exist
48
+ Then spec file with following content should fail:
49
+ """ruby
50
+ describe Student do
51
+ fake(:library)
52
+
53
+ it "studies using books from library" do
54
+ student = Student.new(library)
55
+
56
+ student.study("Moby Dick")
57
+
58
+ library.should_not have_received.return_book("Moby Dick")
59
+ end
60
+ end
61
+ """
62
+
63
+ Scenario: Spying on methods with wrong number of arguments
64
+ Then spec file with following content should fail:
65
+ """ruby
66
+ describe Student do
67
+ fake(:library)
68
+
69
+ it "studies using books from library" do
70
+ student = Student.new(library)
71
+
72
+ student.study("Moby Dick", "Sherlock Holmes")
73
+
74
+ library.should_not have_received.checkout("Moby Dick",
75
+ "Sherlock Holmes")
76
+ end
77
+ end
78
+ """
@@ -0,0 +1,68 @@
1
+ Given /^a spec file named "([^"]*)" with:$/ do |file_name, string|
2
+ @spec_file_names ||= []
3
+ @spec_file_names << file_name
4
+
5
+ steps %Q{
6
+ Given a file named "#{file_name}" with:
7
+ """ruby
8
+ require 'bogus'
9
+ require 'bogus/rspec'
10
+ require 'rr'
11
+
12
+ require_relative 'foo'
13
+
14
+ RSpec.configure do |config|
15
+ config.mock_with :rr
16
+ end
17
+
18
+ #{string}
19
+ """
20
+ }
21
+ end
22
+
23
+ Then /^the specs should fail$/ do
24
+ steps %Q{
25
+ Then the exit status should be 1
26
+ }
27
+ end
28
+
29
+ Then /^all the specs should pass$/ do
30
+ steps %Q{
31
+ Then the exit status should be 0
32
+ }
33
+ end
34
+
35
+ When /^I run spec with the following content:$/ do |string|
36
+ file_name = 'foo_spec.rb'
37
+
38
+ steps %Q{
39
+ Given a spec file named "#{file_name}" with:
40
+ """ruby
41
+ #{string}
42
+ """
43
+ }
44
+
45
+ steps %Q{
46
+ When I run `rspec #{@spec_file_names.join(' ')}`
47
+ }
48
+ end
49
+
50
+ Then /^spec file with following content should pass:$/ do |string|
51
+ steps %Q{
52
+ When I run spec with the following content:
53
+ """ruby
54
+ #{string}
55
+ """
56
+ Then all the specs should pass
57
+ }
58
+ end
59
+
60
+ Then /^spec file with following content should fail:$/ do |string|
61
+ steps %Q{
62
+ When I run spec with the following content:
63
+ """ruby
64
+ #{string}
65
+ """
66
+ Then the specs should fail
67
+ }
68
+ end
@@ -0,0 +1 @@
1
+ require 'aruba/cucumber'
@@ -0,0 +1,35 @@
1
+ require 'dependor'
2
+
3
+ module Bogus
4
+ autoload :AddsRecording, 'bogus/adds_recording'
5
+ autoload :Configuration, 'bogus/configuration'
6
+ autoload :ContractNotFulfilled, 'bogus/contract_not_fulfilled'
7
+ autoload :ConvertsNameToClass, 'bogus/converts_name_to_class'
8
+ autoload :CopiesClasses, 'bogus/copies_classes'
9
+ autoload :CreatesFakes, 'bogus/creates_fakes'
10
+ autoload :Double, 'bogus/double'
11
+ autoload :Fake, 'bogus/fake'
12
+ autoload :FakeRegistry, 'bogus/fake_registry'
13
+ autoload :Injector, 'bogus/injector'
14
+ autoload :Interaction, 'bogus/interaction'
15
+ autoload :InteractionPresenter, 'bogus/interaction_presenter'
16
+ autoload :InteractionsRepository, 'bogus/interactions_repository'
17
+ autoload :InvocationMatcher, 'bogus/invocation_matcher'
18
+ autoload :MethodStringifier, 'bogus/method_stringifier'
19
+ autoload :MockingDSL, 'bogus/rspec_extensions'
20
+ autoload :OverwritesClasses, 'bogus/overwrites_classes'
21
+ autoload :ProxyClass, 'bogus/proxy_class'
22
+ autoload :PublicMethods, 'bogus/public_methods'
23
+ autoload :RRProxy, 'bogus/rr_proxy'
24
+ autoload :RSpecExtensions, 'bogus/rspec_extensions'
25
+ autoload :RecordInteractions, 'bogus/record_interactions'
26
+ autoload :RecordingProxy, 'bogus/recording_proxy'
27
+ autoload :RecordsDoubleInteractions, 'bogus/records_double_interactions'
28
+ autoload :RegistersCreatedFakes, 'bogus/registers_created_fakes'
29
+ autoload :Takes, 'bogus/takes'
30
+ autoload :VERSION, 'bogus/version'
31
+ autoload :VerifiesContracts, 'bogus/verifies_contracts'
32
+ autoload :VerifiesStubDefinition, 'bogus/verifies_stub_definition'
33
+
34
+ extend PublicMethods
35
+ end
@@ -0,0 +1,11 @@
1
+ class Bogus::AddsRecording
2
+ extend Bogus::Takes
3
+
4
+ takes :converts_name_to_class, :create_proxy_class, :overwrites_classes
5
+
6
+ def add(name)
7
+ klass = converts_name_to_class.convert(name)
8
+ new_klass = create_proxy_class.call(name, klass)
9
+ overwrites_classes.overwrite(klass, new_klass)
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ module Bogus
2
+ class Configuration
3
+ attr_writer :search_modules
4
+
5
+ def search_modules
6
+ @search_modules ||= [Object]
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ module Bogus
2
+ class ContractNotFulfilled < StandardError
3
+ attr_reader :interactions
4
+
5
+ def initialize(interactions)
6
+ @interactions = interactions
7
+ super(message)
8
+ end
9
+
10
+ def message
11
+ interactions.map { |fake_name, missed| missed_for_fake(fake_name, missed) }.join("\n")
12
+ end
13
+
14
+ private
15
+
16
+ def missed_for_fake(fake_name, missed)
17
+ "Contract not fullfilled for #{fake_name}:\n#{missed_interactions(missed)}"
18
+ end
19
+
20
+ def missed_interactions(missed)
21
+ missed.map { |i| " - #{InteractionPresenter.new(i)}" }.join("\n")
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,31 @@
1
+ module Bogus
2
+ class ConvertsNameToClass
3
+ extend Takes
4
+
5
+ class CanNotFindClass < RuntimeError; end
6
+
7
+ takes :search_modules
8
+
9
+ def convert(name)
10
+ class_name = camelize(name)
11
+ klass = nil
12
+
13
+ @search_modules.each do |mod|
14
+ klass = mod.const_get(class_name) rescue nil
15
+ break if klass
16
+ end
17
+
18
+ raise CanNotFindClass.new("Can not locate class for name: #{name}") unless klass
19
+
20
+ klass
21
+ end
22
+
23
+ private
24
+
25
+ def camelize(symbol)
26
+ string = symbol.to_s
27
+ string = string.gsub(/_\w/) { |match| match[1].upcase }
28
+ return string.gsub(/^\w/) { |match| match.upcase }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,44 @@
1
+ module Bogus
2
+ class CopiesClasses
3
+ extend Takes
4
+
5
+ takes :method_stringifier
6
+
7
+ def copy(klass)
8
+ copy_class = Class.new(Bogus::Fake)
9
+
10
+ copy_class.__copied_class__ = klass
11
+ copy_instance_methods(klass, copy_class)
12
+ copy_class_methods(klass, copy_class)
13
+
14
+ return copy_class
15
+ end
16
+
17
+ private
18
+
19
+ def copy_instance_methods(klass, copy_class)
20
+ instance_methods = klass.instance_methods - Object.instance_methods
21
+
22
+ instance_methods.each do |name|
23
+ copy_class.class_eval(method_as_string(klass.instance_method(name)))
24
+ end
25
+ end
26
+
27
+ def copy_class_methods(klass, copy_class)
28
+ klass_methods = klass.methods - Class.methods
29
+
30
+ klass_methods.each do |name|
31
+ copy_class.instance_eval(method_as_string(klass.method(name)))
32
+ end
33
+ end
34
+
35
+ def method_as_string(method)
36
+ args = @method_stringifier.arguments_as_string(method.parameters)
37
+ args_no_defaults = args.gsub(' = {}', '')
38
+
39
+ @method_stringifier.stringify(method,
40
+ "__record__(:#{method.name}, #{args_no_defaults})")
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,32 @@
1
+ module Bogus
2
+ class CreatesFakes
3
+ class UnknownMode < RuntimeError; end
4
+
5
+ extend Takes
6
+
7
+ takes :copies_classes, :converts_name_to_class
8
+
9
+ def create(name, opts = {}, &block)
10
+ klass = self.klass(name, &block)
11
+ klass_copy = copies_classes.copy(klass)
12
+
13
+ mode = opts.fetch(:as, :instance)
14
+
15
+ case mode
16
+ when :instance
17
+ return klass_copy.new
18
+ when :class
19
+ return klass_copy
20
+ else
21
+ raise UnknownMode.new("Unknown fake creation mode: #{mode}. Allowed values are :instance, :class")
22
+ end
23
+ end
24
+
25
+ protected
26
+
27
+ def klass(name, &block)
28
+ return block.call if block_given?
29
+ converts_name_to_class.convert(name)
30
+ end
31
+ end
32
+ end