bogus 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile.lock +19 -1
  3. data/Guardfile +0 -7
  4. data/Guardfile.cucumber +9 -0
  5. data/README.md +42 -6
  6. data/bogus.gemspec +3 -0
  7. data/features/.nav +5 -2
  8. data/features/authors.md +10 -2
  9. data/features/changelog.md +11 -0
  10. data/features/configuration/fake_ar_attributes.feature +51 -0
  11. data/features/configuration/readme.md +7 -0
  12. data/features/{configuration_options.feature → configuration/search_modules.feature} +2 -2
  13. data/features/fakes/anonymous_doubles.feature +11 -7
  14. data/features/fakes/fake_objects.feature +0 -16
  15. data/lib/bogus/active_record_accessors.rb +41 -0
  16. data/lib/bogus/class_methods.rb +1 -1
  17. data/lib/bogus/configuration.rb +1 -0
  18. data/lib/bogus/constructor_methods.rb +18 -0
  19. data/lib/bogus/copies_methods.rb +7 -5
  20. data/lib/bogus/creates_fakes.rb +1 -1
  21. data/lib/bogus/creates_fakes_with_stubbed_methods.rb +1 -1
  22. data/lib/bogus/ensures_all_interactions_satisfied.rb +0 -2
  23. data/lib/bogus/fake.rb +8 -0
  24. data/lib/bogus/injector.rb +10 -0
  25. data/lib/bogus/instance_methods.rb +1 -1
  26. data/lib/bogus/interaction.rb +22 -3
  27. data/lib/bogus/interaction_presenter.rb +1 -1
  28. data/lib/bogus/makes_ducks.rb +5 -6
  29. data/lib/bogus/makes_subtypes.rb +1 -1
  30. data/lib/bogus/method_stringifier.rb +16 -8
  31. data/lib/bogus/record_interactions.rb +1 -1
  32. data/lib/bogus/records_double_interactions.rb +8 -7
  33. data/lib/bogus/registers_created_fakes.rb +10 -8
  34. data/lib/bogus/responds_to_everything.rb +8 -1
  35. data/lib/bogus/shadow.rb +3 -2
  36. data/lib/bogus/undefined_return_value.rb +11 -0
  37. data/lib/bogus/verifies_contracts.rb +12 -10
  38. data/lib/bogus/verifies_stub_definition.rb +37 -41
  39. data/lib/bogus/version.rb +1 -1
  40. data/spec/bogus/copies_classes_spec.rb +13 -6
  41. data/spec/bogus/creates_fakes_spec.rb +1 -1
  42. data/spec/bogus/fake_ar_attributes_spec.rb +72 -0
  43. data/spec/bogus/faking_factories_spec.rb +83 -0
  44. data/spec/bogus/frozen_fakes_spec.rb +59 -0
  45. data/spec/bogus/interaction_spec.rb +4 -0
  46. data/spec/bogus/method_copiers_spec.rb +26 -0
  47. data/spec/bogus/mocking_dsl_spec.rb +7 -3
  48. data/spec/bogus/overwrites_methods_spec.rb +2 -2
  49. data/spec/bogus/record_interactions_spec.rb +1 -1
  50. data/spec/bogus/ruby_2_support_spec.rb +96 -0
  51. data/spec/bogus/shadow_spec.rb +10 -8
  52. data/spec/bogus/verifies_stub_definition_spec.rb +1 -1
  53. data/spec/spec_helper.rb +1 -1
  54. data/spec/support/matchers.rb +3 -0
  55. metadata +76 -66
@@ -0,0 +1,18 @@
1
+ module Bogus
2
+ class CopiesConstructor
3
+ extend Takes
4
+ takes :method_stringifier, :instance_methods, :class_methods
5
+
6
+ def copy(from, into)
7
+ return unless from.is_a?(Class)
8
+ initializer = instance_methods.call(from).get(:initialize)
9
+ body = body(initializer)
10
+ class_methods.call(into).define(body)
11
+ end
12
+
13
+ def body(initializer)
14
+ body = method_stringifier.stringify(initializer, "super")
15
+ body.gsub("initialize", "new")
16
+ end
17
+ end
18
+ end
@@ -1,14 +1,16 @@
1
1
  module Bogus
2
2
  class CopiesMethods
3
- extend Bogus::Takes
3
+ extend Takes
4
4
 
5
5
  takes :makes_substitute_methods,
6
- :instance_methods,
7
- :class_methods
6
+ :method_copiers,
7
+ :copies_constructor
8
8
 
9
9
  def copy(from, into)
10
- copy_methods(from, into, instance_methods)
11
- copy_methods(from, into, class_methods)
10
+ method_copiers.each do |copier|
11
+ copy_methods(from, into, copier)
12
+ end
13
+ copies_constructor.copy(from, into)
12
14
  end
13
15
 
14
16
  private
@@ -15,7 +15,7 @@ module Bogus
15
15
 
16
16
  case mode
17
17
  when :instance
18
- return klass_copy.new
18
+ return klass_copy.__create__
19
19
  when :class
20
20
  return klass_copy
21
21
  else
@@ -1,6 +1,6 @@
1
1
  module Bogus
2
2
  class CreatesFakesWithStubbedMethods
3
- extend Bogus::Takes
3
+ extend Takes
4
4
 
5
5
  takes :multi_stubber, :creates_fakes,
6
6
  :responds_to_everything, :fake_configuration
@@ -1,7 +1,5 @@
1
1
  module Bogus
2
2
  class EnsuresAllInteractionsSatisfied
3
- extend Bogus::Takes
4
-
5
3
  def ensure_satisfied!(objects)
6
4
  unsatisfied = unsatisfied_interactions(objects)
7
5
  return if unsatisfied.empty?
data/lib/bogus/fake.rb CHANGED
@@ -10,6 +10,7 @@ module Bogus
10
10
  extend FakeObject
11
11
 
12
12
  def initialize(*args)
13
+ __shadow__
13
14
  end
14
15
 
15
16
  def to_s
@@ -27,6 +28,13 @@ module Bogus
27
28
  class << self
28
29
  attr_accessor :__copied_class__
29
30
 
31
+ alias :__create__ :new
32
+
33
+ def new(*args, &block)
34
+ __record__(:new, *args, &block)
35
+ __create__
36
+ end
37
+
30
38
  def name
31
39
  __copied_class__.name
32
40
  end
@@ -44,6 +44,16 @@ module Bogus
44
44
  inject(ClassMethods, klass: klass)
45
45
  end
46
46
 
47
+ def active_record_accessors(klass)
48
+ inject(ActiveRecordAccessors, klass: klass)
49
+ end
50
+
51
+ def method_copiers
52
+ copiers = [method(:class_methods), method(:instance_methods)]
53
+ copiers << method(:active_record_accessors) if configuration.fake_ar_attributes
54
+ copiers
55
+ end
56
+
47
57
  def have_received_matcher
48
58
  inject(HaveReceivedMatcher)
49
59
  end
@@ -1,6 +1,6 @@
1
1
  module Bogus
2
2
  class InstanceMethods
3
- extend Bogus::Takes
3
+ extend Takes
4
4
  takes :klass
5
5
 
6
6
  def all
@@ -19,15 +19,26 @@ module Bogus
19
19
  end
20
20
 
21
21
  def args
22
- super.reject { |arg| arg.eql?(Bogus::DefaultValue) }
22
+ args = super.map { |arg| remove_default_values_from_hash(arg) }
23
+ args.reject { |arg| arg.eql?(DefaultValue) }
23
24
  end
24
25
 
25
26
  private
26
27
 
27
28
  def same_args?(other)
28
29
  return true if any_args? || other.any_args?
29
- return false unless args.size == other.args.size
30
- args.zip(other.args).all?{|a1, a2| a1 == a2 || a2 == a1}
30
+
31
+ other_args = normalize_other_args(args, other.args)
32
+ return false unless args.size == other_args.size
33
+ args.zip(other_args).all?{|a1, a2| a1 == a2 || a2 == a1}
34
+ end
35
+
36
+ def normalize_other_args(args, other_args)
37
+ if args.last.is_a?(Hash) && !other_args.last.is_a?(Hash)
38
+ other_args + [{}]
39
+ else
40
+ other_args
41
+ end
31
42
  end
32
43
 
33
44
  def same_result?(other)
@@ -40,5 +51,13 @@ module Bogus
40
51
  rescue => e
41
52
  self.error = e.class
42
53
  end
54
+
55
+ def remove_default_values_from_hash(arg)
56
+ if arg.is_a?(Hash)
57
+ arg.reject { |_, val| val.eql?(DefaultValue) }
58
+ else
59
+ arg
60
+ end
61
+ end
43
62
  end
44
63
  end
@@ -1,6 +1,6 @@
1
1
  module Bogus
2
2
  class InteractionPresenter
3
- extend Bogus::Takes
3
+ extend Takes
4
4
 
5
5
  takes :interaction
6
6
 
@@ -1,16 +1,15 @@
1
1
  module Bogus
2
2
  class MakesDucks
3
- extend Bogus::Takes
3
+ extend Takes
4
4
 
5
- takes :class_methods, :instance_methods, :makes_subtypes
5
+ takes :method_copiers, :makes_subtypes
6
6
 
7
7
  def make(first_class, *classes)
8
8
  duck = makes_subtypes.make(first_class)
9
9
  classes.each do |klass|
10
- remove_methods(class_methods.call(duck),
11
- class_methods.call(klass))
12
- remove_methods(instance_methods.call(duck),
13
- instance_methods.call(klass))
10
+ method_copiers.each do |copier|
11
+ remove_methods(copier.call(duck), copier.call(klass))
12
+ end
14
13
  end
15
14
  duck
16
15
  end
@@ -1,6 +1,6 @@
1
1
  module Bogus
2
2
  class MakesSubtypes
3
- extend Bogus::Takes
3
+ extend Takes
4
4
 
5
5
  takes :copies_methods
6
6
 
@@ -10,21 +10,29 @@ module Bogus
10
10
  end
11
11
 
12
12
  def arguments_as_string(arguments)
13
- arguments = fill_in_missing_names(arguments)
14
- arguments.map{|type, name| argument_to_string(name, type) }.compact.join(', ')
13
+ stringify_arguments(arguments, DefaultValue)
15
14
  end
16
15
 
17
16
  def argument_values(arguments)
18
- arguments_as_string(arguments).gsub(" = Bogus::DefaultValue", '')
17
+ stringify_arguments(arguments)
19
18
  end
20
19
 
21
- def argument_to_string(name, type)
20
+ private
21
+
22
+ def stringify_arguments(arguments, default = nil)
23
+ fill_in_missing_names(arguments).map do |type, name|
24
+ argument_to_string(name, type, default)
25
+ end.join(', ')
26
+ end
27
+
28
+ def argument_to_string(name, type, default)
22
29
  case type
23
30
  when :block then "&#{name}"
24
- when :key then "#{name}: #{name}"
25
- when :opt then "#{name} = Bogus::DefaultValue"
26
- when :req then name
27
- when :rest then "*#{name}"
31
+ when :key then default ? "#{name}: #{default}" : "#{name}: #{name}"
32
+ when :opt then default ? "#{name} = #{default}" : name
33
+ when :req then name
34
+ when :rest then "*#{name}"
35
+ when :keyrest then "**#{name}"
28
36
  else raise "unknown argument type: #{type}"
29
37
  end
30
38
  end
@@ -1,7 +1,7 @@
1
1
  module Bogus
2
2
  module RecordInteractions
3
3
  def __shadow__
4
- @__shadow__ ||= Shadow.new{ self }
4
+ @__shadow__ ||= Shadow.new
5
5
  end
6
6
 
7
7
  def __record__(method, *args, &block)
@@ -1,11 +1,12 @@
1
- class Bogus::RecordsDoubleInteractions
2
- extend Bogus::Takes
1
+ module Bogus
2
+ class RecordsDoubleInteractions
3
+ extend Takes
3
4
 
4
- takes :doubled_interactions, :fake_registry
5
+ takes :doubled_interactions, :fake_registry
5
6
 
6
- def record(object, method_name, args, &block)
7
- fake_name = fake_registry.name(object)
8
- doubled_interactions.record(fake_name, method_name, *args, &block) if fake_name
7
+ def record(object, method_name, args, &block)
8
+ fake_name = fake_registry.name(object)
9
+ doubled_interactions.record(fake_name, method_name, *args, &block) if fake_name
10
+ end
9
11
  end
10
-
11
12
  end
@@ -1,12 +1,14 @@
1
- class Bogus::RegistersCreatedFakes
2
- extend Bogus::Takes
1
+ module Bogus
2
+ class RegistersCreatedFakes
3
+ extend Takes
3
4
 
4
- takes :creates_fakes, :fake_registry, :double_tracker
5
+ takes :creates_fakes, :fake_registry, :double_tracker
5
6
 
6
- def create(name, opts = {}, &block)
7
- fake = creates_fakes.create(name, opts, &block)
8
- fake_registry.store(name, fake)
9
- double_tracker.track(fake)
10
- fake
7
+ def create(name, opts = {}, &block)
8
+ fake = creates_fakes.create(name, opts, &block)
9
+ fake_registry.store(name, fake)
10
+ double_tracker.track(fake)
11
+ fake
12
+ end
11
13
  end
12
14
  end
@@ -1,11 +1,18 @@
1
1
  module Bogus
2
2
  class RespondsToEverything
3
+ include FakeObject
4
+ include RecordInteractions
5
+
6
+ def initialize
7
+ __shadow__
8
+ end
9
+
3
10
  def respond_to?(method)
4
11
  true
5
12
  end
6
13
 
7
14
  def method_missing(name, *args, &block)
8
- self
15
+ __record__(name, *args, &block)
9
16
  end
10
17
  end
11
18
  end
data/lib/bogus/shadow.rb CHANGED
@@ -2,10 +2,10 @@ module Bogus
2
2
  class Shadow
3
3
  attr_reader :calls
4
4
 
5
- def initialize(&default_return_value)
5
+ def initialize
6
6
  @calls = []
7
7
  @stubs = []
8
- @defaults = Hash.new(default_return_value)
8
+ @defaults = {}
9
9
  @required = Set.new
10
10
  end
11
11
 
@@ -54,6 +54,7 @@ module Bogus
54
54
  def return_value(interaction)
55
55
  _, return_value = @stubs.reverse.find{|i, v| i == interaction}
56
56
  return_value ||= @defaults[interaction.method]
57
+ return_value ||= proc{ UndefinedReturnValue.new(interaction) }
57
58
  return_value.call
58
59
  end
59
60
  end
@@ -0,0 +1,11 @@
1
+ module Bogus
2
+ class UndefinedReturnValue
3
+ def initialize(interaction)
4
+ @interaction = InteractionPresenter.new(interaction)
5
+ end
6
+
7
+ def to_s
8
+ "#<UndefinedReturnValue for #{@interaction}>"
9
+ end
10
+ end
11
+ end
@@ -1,16 +1,18 @@
1
- class Bogus::VerifiesContracts
2
- extend Bogus::Takes
1
+ module Bogus
2
+ class VerifiesContracts
3
+ extend Takes
3
4
 
4
- takes :doubled_interactions, :real_interactions
5
+ takes :doubled_interactions, :real_interactions
5
6
 
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
7
+ def verify(fake_name)
8
+ missed = doubled_interactions.for_fake(fake_name).reject do |interaction|
9
+ real_interactions.recorded?(fake_name, interaction)
10
+ end
10
11
 
11
- unless missed.empty?
12
- actual = real_interactions.for_fake(fake_name)
13
- raise Bogus::ContractNotFulfilled.new(fake_name, missed: missed, actual: actual)
12
+ unless missed.empty?
13
+ actual = real_interactions.for_fake(fake_name)
14
+ raise Bogus::ContractNotFulfilled.new(fake_name, missed: missed, actual: actual)
15
+ end
14
16
  end
15
17
  end
16
18
  end
@@ -1,43 +1,39 @@
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
- return unless object.methods.include?(method_name)
9
- return if any_args?(args)
10
- method = object.method(method_name)
11
- wrong_number_of_arguments!(method, args) if under_number_of_required_arguments?(method, args.size)
12
- wrong_number_of_arguments!(method, args) if over_number_of_allowed_arguments?(method, args.size)
13
- end
14
-
15
- private
16
-
17
- def wrong_number_of_arguments!(method, args)
18
- args_string = method_stringifier.arguments_as_string(method.parameters)
19
- raise ArgumentError, "tried to stub #{method.name}(#{args_string}) with #{args.size} arguments"
20
- end
21
-
22
- def stubbing_non_existent_method!(object, method_name)
23
- raise NameError, "#{object.inspect} does not respond to #{method_name}"
24
- end
25
-
26
- def under_number_of_required_arguments?(method, args_count)
27
- number_of_arguments = method.arity
28
- number_of_arguments = -number_of_arguments - 1 if number_of_arguments < 0
29
-
30
- args_count < number_of_arguments
31
- end
32
-
33
- def over_number_of_allowed_arguments?(method, args_count)
34
- return false if method.parameters.find{|type, name| type == :rest}
35
- number_of_arguments = method.parameters.count{|type, name| [:key, :opt, :req].include?(type) }
36
-
37
- args_count > number_of_arguments
38
- end
39
-
40
- def any_args?(args)
41
- [Bogus::AnyArgs] == args
1
+ module Bogus
2
+ class VerifiesStubDefinition
3
+ extend Takes
4
+
5
+ takes :method_stringifier
6
+
7
+ def verify!(object, method_name, args)
8
+ stubbing_non_existent_method!(object, method_name) unless object.respond_to?(method_name)
9
+ return unless object.methods.include?(method_name)
10
+ return if any_args?(args)
11
+ method = object.method(method_name)
12
+ verify_call!(method, args)
13
+ end
14
+
15
+ private
16
+
17
+ def verify_call!(method, args)
18
+ object = Object.new
19
+ fake_method = method_stringifier.stringify(method, "")
20
+ object.instance_eval(fake_method)
21
+ object.send(method.name, *args)
22
+ rescue ArgumentError
23
+ wrong_arguments!(method, args)
24
+ end
25
+
26
+ def wrong_arguments!(method, args)
27
+ args_string = method_stringifier.arguments_as_string(method.parameters)
28
+ raise ArgumentError, "tried to stub #{method.name}(#{args_string}) with arguments: #{args.map(&:inspect).join(",")}"
29
+ end
30
+
31
+ def stubbing_non_existent_method!(object, method_name)
32
+ raise NameError, "#{object.inspect} does not respond to #{method_name}"
33
+ end
34
+
35
+ def any_args?(args)
36
+ [Bogus::AnyArgs] == args
37
+ end
42
38
  end
43
39
  end