verified_double 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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