verified_double 0.1.1 → 0.2.0

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 (41) hide show
  1. data/CHANGELOG.markdown +12 -0
  2. data/README.md +25 -14
  3. data/features/CHANGELOG.markdown +12 -0
  4. data/features/readme.md +25 -14
  5. data/features/verified_mocks.feature +4 -2
  6. data/lib/verified_double.rb +10 -1
  7. data/lib/verified_double/matchers.rb +2 -2
  8. data/lib/verified_double/method_signature.rb +5 -5
  9. data/lib/verified_double/method_signature/boolean_value.rb +9 -0
  10. data/lib/verified_double/method_signature/class_value.rb +17 -0
  11. data/lib/verified_double/method_signature/instance_double_value.rb +9 -0
  12. data/lib/verified_double/method_signature/instance_value.rb +17 -0
  13. data/lib/verified_double/method_signature/recording_double_class_value.rb +9 -0
  14. data/lib/verified_double/method_signature/rspec_double_value.rb +9 -0
  15. data/lib/verified_double/method_signature/value.rb +37 -0
  16. data/lib/verified_double/method_signatures_report.rb +10 -3
  17. data/lib/verified_double/parse_method_signature.rb +2 -2
  18. data/lib/verified_double/recorded_method_signature.rb +9 -0
  19. data/lib/verified_double/recording_double.rb +13 -4
  20. data/lib/verified_double/stack_frame.rb +11 -0
  21. data/lib/verified_double/version.rb +1 -1
  22. data/spec/spec_helper.rb +1 -0
  23. data/spec/verified_double/matchers_spec.rb +1 -1
  24. data/spec/verified_double/method_signature/boolean_value_spec.rb +11 -0
  25. data/spec/verified_double/method_signature/class_value_spec.rb +35 -0
  26. data/spec/verified_double/method_signature/instance_double_value_spec.rb +17 -0
  27. data/spec/verified_double/method_signature/instance_value_spec.rb +39 -0
  28. data/spec/verified_double/method_signature/recording_double_class_value_spec.rb +23 -0
  29. data/spec/verified_double/method_signature/rspec_double_spec.rb +14 -0
  30. data/spec/verified_double/method_signature/value_spec.rb +89 -0
  31. data/spec/verified_double/method_signature_spec.rb +16 -16
  32. data/spec/verified_double/method_signatures_report_spec.rb +14 -12
  33. data/spec/verified_double/parse_method_signature_spec.rb +3 -3
  34. data/spec/verified_double/recorded_method_signature_spec.rb +23 -0
  35. data/spec/verified_double/recording_double_spec.rb +103 -77
  36. data/spec/verified_double/stack_frame_spec.rb +15 -0
  37. data/spec/verified_double_spec.rb +1 -1
  38. metadata +29 -7
  39. data/lib/verified_double/method_signature_value.rb +0 -45
  40. data/spec/unit_helper.rb +0 -16
  41. data/spec/verified_double/method_signature_value_spec.rb +0 -126
@@ -1,3 +1,15 @@
1
+ 0.2.0 - 2013-06-30
2
+ ------------------
3
+
4
+ * [#4] Add stack frames and relish app link to output.
5
+ * [#12] RecordingDouble#class should be the class of the object being doubled.
6
+ * [#16] Fix handling of doubles passed as method signature values.
7
+
8
+ Known issues:
9
+
10
+ * Stack frames sometimes point to verified_double gem instead of where the mock was recorded.
11
+
12
+
1
13
  0.1.1 - 2013-06-26
2
14
  ------------------
3
15
 
data/README.md CHANGED
@@ -2,34 +2,45 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.org/gsmendoza/verified_double.png)](https://travis-ci.org/gsmendoza/verified_double)
4
4
 
5
- VerifiedDouble verifies mocks made in the test suite by checking if there are tests against them.
5
+ VerifiedDouble is a gem for verifying rspec mocks. The gem works similar to [rspec-fire](https://github.com/xaviershay/rspec-fire). However, instead of checking if the doubled classes respond to the methods, the gem looks for tests confirming if those mocks are valid.
6
6
 
7
7
  For example, let's say I mocked the created_at method of a model like this:
8
8
 
9
9
  item = VerifiedDouble.of_instance(Item)
10
10
  item.should_receive(:created_at).and_return(Time.now)
11
11
 
12
- The item double records its :created_at call. When
13
- running the tests, the gem looks for a test that confirms if you can
14
- indeed call #created_at on Item. The test should also ensure that the method
15
- returns a Time object. Since this is hard to automate, the gem just looks
16
- for a test with a tag saying it verifies that contract:
12
+ When running the tests, the gem looks for a "contract test" tagged with the method's signature. This test should ensure that calling #created_at on Item will return a Time object.
17
13
 
18
14
  it "tests something", verifies_contract: 'Item#created_at()=>Time' do
19
15
  #...
20
16
  end
21
17
 
22
- If this test does not exist, the gem will complain that the mock is not
23
- verified.
18
+ If this test does not exist, the gem will complain that the mock is not verified.
24
19
 
25
- More information at https://www.relishapp.com/gsmendoza/verified-double.
20
+ I got the idea from http://www.infoq.com/presentations/integration-tests-scam, an old (2009) talk that still has some fresh insights on dealing with API changes in your mocked tests.
26
21
 
27
- References
28
- ----------
22
+ You can learn more about using the gem at https://www.relishapp.com/gsmendoza/verified-double.
29
23
 
30
- 1. http://www.confreaks.com/videos/2452-railsconf2013-the-magic-tricks-of-testing
31
- 2. https://www.relishapp.com/bogus/bogus/v/0-0-3/docs/contract-tests
32
- 3. http://www.infoq.com/presentations/integration-tests-scam
24
+ Actively tested against
25
+ -----------------------
26
+
27
+ * Ruby 1.9.3
28
+ * RSpec 2.13
29
+
30
+
31
+ Alternatives
32
+ ------------
33
+
34
+ [Bogus](https://www.relishapp.com/bogus/bogus/v/0-0-3/docs/) is the first gem to implement contract tests. It doesn't rely on rspec tags to verify contracts, so it's probably a lot smarter than VerifiedDouble :) However, I wasn't able to try it out on my own projects because of its own rr-like mock adapter. But do check it out!
35
+
36
+ Caveats
37
+ -------
38
+
39
+ VerifiedDouble is still in its infancy, but I hope it's usable for the most common cases.
40
+
41
+ * If you check the RelishApp doc, the gem only supports a subset of rspec-mock's API. Please post an issue at http://github.com/gsmendoza/verified_double if you need support for any particular rspec-mock API.
42
+
43
+ * The [method documentation](http://rubydoc.info/gems/verified_double) is pretty empty at this point :p I'm planning to use yard-spec to document the methods but that gem doesn't support rspec context blocks. I'll try to work on that soon.
33
44
 
34
45
  Special thanks
35
46
  --------------
@@ -1,3 +1,15 @@
1
+ 0.2.0 - 2013-06-30
2
+ ------------------
3
+
4
+ * [#4] Add stack frames and relish app link to output.
5
+ * [#12] RecordingDouble#class should be the class of the object being doubled.
6
+ * [#16] Fix handling of doubles passed as method signature values.
7
+
8
+ Known issues:
9
+
10
+ * Stack frames sometimes point to verified_double gem instead of where the mock was recorded.
11
+
12
+
1
13
  0.1.1 - 2013-06-26
2
14
  ------------------
3
15
 
@@ -2,34 +2,45 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.org/gsmendoza/verified_double.png)](https://travis-ci.org/gsmendoza/verified_double)
4
4
 
5
- VerifiedDouble verifies mocks made in the test suite by checking if there are tests against them.
5
+ VerifiedDouble is a gem for verifying rspec mocks. The gem works similar to [rspec-fire](https://github.com/xaviershay/rspec-fire). However, instead of checking if the doubled classes respond to the methods, the gem looks for tests confirming if those mocks are valid.
6
6
 
7
7
  For example, let's say I mocked the created_at method of a model like this:
8
8
 
9
9
  item = VerifiedDouble.of_instance(Item)
10
10
  item.should_receive(:created_at).and_return(Time.now)
11
11
 
12
- The item double records its :created_at call. When
13
- running the tests, the gem looks for a test that confirms if you can
14
- indeed call #created_at on Item. The test should also ensure that the method
15
- returns a Time object. Since this is hard to automate, the gem just looks
16
- for a test with a tag saying it verifies that contract:
12
+ When running the tests, the gem looks for a "contract test" tagged with the method's signature. This test should ensure that calling #created_at on Item will return a Time object.
17
13
 
18
14
  it "tests something", verifies_contract: 'Item#created_at()=>Time' do
19
15
  #...
20
16
  end
21
17
 
22
- If this test does not exist, the gem will complain that the mock is not
23
- verified.
18
+ If this test does not exist, the gem will complain that the mock is not verified.
24
19
 
25
- More information at https://www.relishapp.com/gsmendoza/verified-double.
20
+ I got the idea from http://www.infoq.com/presentations/integration-tests-scam, an old (2009) talk that still has some fresh insights on dealing with API changes in your mocked tests.
26
21
 
27
- References
28
- ----------
22
+ You can learn more about using the gem at https://www.relishapp.com/gsmendoza/verified-double.
29
23
 
30
- 1. http://www.confreaks.com/videos/2452-railsconf2013-the-magic-tricks-of-testing
31
- 2. https://www.relishapp.com/bogus/bogus/v/0-0-3/docs/contract-tests
32
- 3. http://www.infoq.com/presentations/integration-tests-scam
24
+ Actively tested against
25
+ -----------------------
26
+
27
+ * Ruby 1.9.3
28
+ * RSpec 2.13
29
+
30
+
31
+ Alternatives
32
+ ------------
33
+
34
+ [Bogus](https://www.relishapp.com/bogus/bogus/v/0-0-3/docs/) is the first gem to implement contract tests. It doesn't rely on rspec tags to verify contracts, so it's probably a lot smarter than VerifiedDouble :) However, I wasn't able to try it out on my own projects because of its own rr-like mock adapter. But do check it out!
35
+
36
+ Caveats
37
+ -------
38
+
39
+ VerifiedDouble is still in its infancy, but I hope it's usable for the most common cases.
40
+
41
+ * If you check the RelishApp doc, the gem only supports a subset of rspec-mock's API. Please post an issue at http://github.com/gsmendoza/verified_double if you need support for any particular rspec-mock API.
42
+
43
+ * The [method documentation](http://rubydoc.info/gems/verified_double) is pretty empty at this point :p I'm planning to use yard-spec to document the methods but that gem doesn't support rspec context blocks. I'll try to work on that soon.
33
44
 
34
45
  Special thanks
35
46
  --------------
@@ -93,7 +93,8 @@ Feature: 01. Verified mocks
93
93
  Then I should be informed that the mock is unverified:
94
94
  """
95
95
  The following mocks are not verified:
96
- Collaborator#some_method(SomeInput)=>SomeOutput
96
+
97
+ 1. Collaborator#some_method(SomeInput)=>SomeOutput
97
98
  """
98
99
 
99
100
  Scenario: Verified class doubles
@@ -147,5 +148,6 @@ Feature: 01. Verified mocks
147
148
  Then I should be informed that the mock is unverified:
148
149
  """
149
150
  The following mocks are not verified:
150
- Collaborator.some_method(SomeInput)=>SomeOutput
151
+
152
+ 1. Collaborator.some_method(SomeInput)=>SomeOutput
151
153
  """
@@ -1,13 +1,22 @@
1
+ require 'active_support/core_ext/string'
1
2
  require 'rspec/mocks'
2
3
 
3
4
  require 'verified_double/boolean'
4
5
  require 'verified_double/matchers'
5
6
  require 'verified_double/method_signature'
6
- require 'verified_double/method_signature_value'
7
+ require 'verified_double/method_signature/value'
8
+ require 'verified_double/method_signature/instance_value'
9
+ require 'verified_double/method_signature/boolean_value'
10
+ require 'verified_double/method_signature/class_value'
11
+ require 'verified_double/method_signature/instance_double_value'
12
+ require 'verified_double/method_signature/recording_double_class_value'
13
+ require 'verified_double/method_signature/rspec_double_value'
7
14
  require 'verified_double/method_signatures_report'
8
15
  require 'verified_double/parse_method_signature'
16
+ require 'verified_double/recorded_method_signature'
9
17
  require 'verified_double/recording_double'
10
18
  require 'verified_double/relays_to_internal_double_returning_self'
19
+ require 'verified_double/stack_frame'
11
20
 
12
21
  module VerifiedDouble
13
22
  extend RSpec::Mocks::ExampleMethods
@@ -12,7 +12,7 @@ module VerifiedDouble
12
12
 
13
13
  raise CannotHandleMultipleReturnValues if method_signature.return_values.size > 1
14
14
 
15
- value = method_signature.return_values.first.as_instance
15
+ value = method_signature.return_values.first.content_as_instance
16
16
  actual.send "#{method_signature.method}=", value
17
17
  actual.send(method_signature.method) == value
18
18
  end
@@ -26,7 +26,7 @@ module VerifiedDouble
26
26
 
27
27
  raise CannotHandleMultipleReturnValues if method_signature.return_values.size > 1
28
28
 
29
- actual.send(method_signature.method).is_a?(method_signature.return_values.first.value)
29
+ actual.send(method_signature.method).is_a?(method_signature.return_values.first.content)
30
30
  end
31
31
  end
32
32
 
@@ -8,14 +8,14 @@ module VerifiedDouble
8
8
  end
9
9
  end
10
10
 
11
- def accepts?(other)
11
+ def belongs_to?(other)
12
12
  self.class_name == other.class_name &&
13
13
  self.method_operator == other.method_operator &&
14
14
  self.method == other.method &&
15
15
  self.args.size == other.args.size &&
16
- (0 ... args.size).all?{|i| self.args[i].accepts?(other.args[i]) } &&
16
+ (0 ... args.size).all?{|i| self.args[i].belongs_to?(other.args[i]) } &&
17
17
  self.return_values.size == other.return_values.size &&
18
- (0 ... return_values.size).all?{|i| self.return_values[i].accepts?(other.return_values[i]) }
18
+ (0 ... return_values.size).all?{|i| self.return_values[i].belongs_to?(other.return_values[i]) }
19
19
  end
20
20
 
21
21
  def args
@@ -42,8 +42,8 @@ module VerifiedDouble
42
42
  end
43
43
 
44
44
  def to_s
45
- args_string = args.map(&:value).map{|v| v || 'nil'}.join(', ')
46
- return_values_string = return_values.map(&:value).map{|v| v || 'nil'}.join(', ')
45
+ args_string = args.map(&:content).map{|v| v || 'nil'}.join(', ')
46
+ return_values_string = return_values.map(&:content).map{|v| v || 'nil'}.join(', ')
47
47
  return_values_string = nil if return_values_string.empty?
48
48
 
49
49
  result = [
@@ -0,0 +1,9 @@
1
+ module VerifiedDouble
2
+ class MethodSignature
3
+ class BooleanValue < InstanceValue
4
+ def content_class
5
+ VerifiedDouble::Boolean
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ module VerifiedDouble
2
+ class MethodSignature
3
+ class ClassValue < Value
4
+ def belongs_to?(other)
5
+ self.content == other.content
6
+ end
7
+
8
+ def content_as_instance
9
+ begin
10
+ content.new
11
+ rescue NoMethodError
12
+ Object.new
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module VerifiedDouble
2
+ class MethodSignature
3
+ class InstanceDoubleValue < InstanceValue
4
+ def content_as_instance
5
+ Value.from(content_class).content_as_instance
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ module VerifiedDouble
2
+ class MethodSignature
3
+ class InstanceValue < Value
4
+ def belongs_to?(other)
5
+ if other.is_a?(MethodSignature::InstanceValue)
6
+ self.content == other.content
7
+ else
8
+ self.content_class.ancestors.include?(other.content)
9
+ end
10
+ end
11
+
12
+ def content_as_instance
13
+ self.content
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module VerifiedDouble
2
+ class MethodSignature
3
+ class RecordingDoubleClassValue < ClassValue
4
+ def belongs_to?(other)
5
+ Value.from(content.doubled_class).belongs_to?(other)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module VerifiedDouble
2
+ class MethodSignature
3
+ class RspecDoubleValue < InstanceDoubleValue
4
+ def content_class
5
+ content.instance_variable_get('@name').constantize
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,37 @@
1
+ module VerifiedDouble
2
+ class MethodSignature
3
+ class Value
4
+ attr_reader :content
5
+
6
+ def self.from(content)
7
+ if content == true || content == false
8
+ BooleanValue.new(content)
9
+ elsif content.is_a?(RSpec::Mocks::Mock)
10
+ RspecDoubleValue.new(content)
11
+ elsif content.respond_to?(:doubled_class)
12
+ if content.class_double?
13
+ RecordingDoubleClassValue.new(content)
14
+ else
15
+ InstanceDoubleValue.new(content)
16
+ end
17
+ elsif content.is_a?(Module)
18
+ ClassValue.new(content)
19
+ else
20
+ InstanceValue.new(content)
21
+ end
22
+ end
23
+
24
+ def initialize(content)
25
+ @content = content
26
+ end
27
+
28
+ def content_class
29
+ content.class
30
+ end
31
+
32
+ def recommended_value
33
+ MethodSignature::Value.from(self.content_class)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -14,7 +14,7 @@ module VerifiedDouble
14
14
  def identify_unverified_signatures
15
15
  @unverified_signatures = @registered_signatures.select{|registered_signature|
16
16
  @verified_signatures.all?{|verified_signature|
17
- ! registered_signature.accepts?(verified_signature) } }
17
+ ! registered_signature.belongs_to?(verified_signature) } }
18
18
  self
19
19
  end
20
20
 
@@ -25,8 +25,15 @@ module VerifiedDouble
25
25
 
26
26
  def output_unverified_signatures
27
27
  if @unverified_signatures.any?
28
- output = ["The following mocks are not verified:" ] + @unverified_signatures.map(&:recommended_verified_signature).map(&:to_s).sort
29
- puts output.join("\n")
28
+ output = [nil, "The following mocks are not verified:" ] +
29
+ @unverified_signatures
30
+ .map(&:recommended_verified_signature)
31
+ .map(&:to_s)
32
+ .sort
33
+ .each_with_index
34
+ .map{|string, i| "#{i+1}. #{string}" } +
35
+ ["For more info, check out https://www.relishapp.com/gsmendoza/verified-double."]
36
+ puts output.join("\n\n")
30
37
  end
31
38
  self
32
39
  end
@@ -9,7 +9,7 @@ module VerifiedDouble
9
9
  def args
10
10
  results = string.scan(/\((.*)\)/)[0]
11
11
  if results
12
- results[0].split(',').map{|arg| MethodSignatureValue.new(eval(arg)) }
12
+ results[0].split(',').map{|arg| MethodSignature::Value.from(eval(arg)) }
13
13
  else
14
14
  []
15
15
  end
@@ -40,7 +40,7 @@ module VerifiedDouble
40
40
  results = string.scan(/=>(.*)/)[0]
41
41
  if results
42
42
  results[0].split(',').map{|return_value|
43
- MethodSignatureValue.new(eval(return_value)) }
43
+ MethodSignature::Value.from(eval(return_value)) }
44
44
  else
45
45
  []
46
46
  end
@@ -0,0 +1,9 @@
1
+ module VerifiedDouble
2
+ class RecordedMethodSignature < MethodSignature
3
+ attr_accessor :stack_frame
4
+
5
+ def to_s
6
+ "#{super}\n # #{stack_frame}"
7
+ end
8
+ end
9
+ end
@@ -21,11 +21,15 @@ module VerifiedDouble
21
21
  end
22
22
 
23
23
  def and_return(return_value)
24
- self.method_signatures.last.return_values = [MethodSignatureValue.new(return_value)]
24
+ self.method_signatures.last.return_values = [MethodSignature::Value.from(return_value)]
25
25
  @double_call.and_return(return_value)
26
26
  self
27
27
  end
28
28
 
29
+ def class
30
+ class_double? ? Class : doubled_class
31
+ end
32
+
29
33
  def class_double?
30
34
  ! double.is_a?(RSpec::Mocks::Mock)
31
35
  end
@@ -34,6 +38,10 @@ module VerifiedDouble
34
38
  __getobj__
35
39
  end
36
40
 
41
+ def doubled_class
42
+ class_name.constantize
43
+ end
44
+
37
45
  def inspect
38
46
  to_s
39
47
  end
@@ -64,7 +72,7 @@ module VerifiedDouble
64
72
 
65
73
  def with(*args)
66
74
  self.method_signatures.last.args =
67
- args.map{|arg| MethodSignatureValue.new(arg) }
75
+ args.map{|arg| MethodSignature::Value.from(arg) }
68
76
  @double_call.with(*args)
69
77
  self
70
78
  end
@@ -72,10 +80,11 @@ module VerifiedDouble
72
80
  private
73
81
 
74
82
  def add_method_signature(method)
75
- method_signature = MethodSignature.new(
83
+ method_signature = RecordedMethodSignature.new(
76
84
  class_name: class_name,
77
85
  method_operator: method_operator,
78
- method: method.to_s)
86
+ method: method.to_s,
87
+ stack_frame: StackFrame.new(caller(2)[0]))
79
88
 
80
89
  self.method_signatures << method_signature
81
90
  end