matahari 0.1.1 → 0.1.2

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 (3) hide show
  1. data/README.rdoc +35 -1
  2. data/lib/matahari/debriefing.rb +26 -25
  3. metadata +7 -2
data/README.rdoc CHANGED
@@ -16,6 +16,7 @@ you to specify all method calls, including uninteresting ones.
16
16
  describe "my object" do
17
17
  obj = MyAwesomeObject.new
18
18
  collaborator = spy(:collaborator)
19
+ obj.collaborator = collaborator
19
20
 
20
21
  obj.my_awesome_method
21
22
 
@@ -24,11 +25,43 @@ you to specify all method calls, including uninteresting ones.
24
25
 
25
26
  See also the cucumber feature, easily viewable at http://relishapp.com/Mortice/matahari
26
27
 
28
+ == CONTEXT:
29
+
30
+ Traditional mocks require you to put expectations (my_mock.should_receive) before action,
31
+ potentially with assertions coming after that. They also do stubbing at the same time as
32
+ setting up expectations (my_mock.should_receive(:something).and_return). These issues
33
+ lead to less readable tests and a tendency to set an expectation when only a stub is
34
+ required, or vice versa.
35
+
36
+ Test spies present an alternative philosophy to traditional mocking by acting as null
37
+ objects and then allowing assertions after the fact on method calls they have received.
38
+ This means that stubbing and asserting on collaborations can be separated and that all
39
+ assertions in a given test go in the same place.
40
+
41
+ The RR project provides test spies, but due to the way it is built (it supports a
42
+ variety of different double strategies), it requires the user to call 'stub' on any
43
+ method which will later be asserted on. As @dchemlimsky puts it, this 'adds the need
44
+ for an extra line, binds these two lines [the stub call and the assertion] together,
45
+ and generally makes [the test] more difficult to understand.'
46
+
47
+ By sticking to spies as a double strategy, matahari is able to avoid the need for a
48
+ similar call in the setup portion of the test - matahari spies simply collect the
49
+ details of all messages passed to them and provide a means for inspecting those
50
+ messages later.
51
+
52
+ === Further reading
53
+ https://gist.github.com/716640 - Conversation between dchelimsky and myself about Ruby test
54
+ doubles
55
+
56
+ http://dannorth.net/2008/09/14/the-end-of-endotesting/ - Article about mockito, which heavily
57
+ influenced the creation of Matahari
58
+
27
59
  == KNOWN ISSUES/TODO:
28
60
 
29
61
  * Stubbing implementation is incredibly basic
30
62
  * Not very usable at all for test/unit users (but this is easily fixed with an assert_received method)
31
63
  * No support for argument matchers - arguments must match exactly via == or not at all
64
+ * We need to be able to remote-control BMWs.
32
65
 
33
66
  == INSTALL:
34
67
 
@@ -36,10 +69,11 @@ See also the cucumber feature, easily viewable at http://relishapp.com/Mortice/m
36
69
 
37
70
  == ACKNOWLEDGEMENTS:
38
71
 
39
- * David Chelimsky, for putting up with my 'robust' criticisms of RSpec mocks
72
+ * David Chelimsky, for putting up with my robust criticisms of RSpec mocks
40
73
  * Aslak Hellesøy, for giving interesting feedback on an early version
41
74
  * Szczepan Faber, for creating Mockito
42
75
  * Brian Takita, for creating RR
76
+ * James Adam, for encouraging people to add CONTEXT sections to their READMEs
43
77
 
44
78
  == LICENSE:
45
79
 
@@ -1,31 +1,14 @@
1
1
  class Debriefing
2
- def initialize(expected_calls = nil)
3
- @expected_calls = expected_calls
2
+ def initialize(expected_call_count = nil)
3
+ @expected_call_count = expected_call_count
4
4
  end
5
5
 
6
6
  def matches?(subject)
7
7
  @subject = subject
8
- invocations_matching_method = subject.invocations.select {|i| i[:method] == @call_to_verify}
9
- method_matched = invocations_matching_method.size > 0
10
- no_args = @args_to_verify.size == 0
11
- @matching_calls = invocations_matching_method.select {|i| i[:args].flatten === @args_to_verify}.size
12
- if @expected_calls
13
- checked_number_of_calls = true
14
- args_match = @matching_calls == @expected_calls
15
- else
16
- args_match = @matching_calls > 0
17
- end
18
- matching = if checked_number_of_calls
19
- args_match
20
- else
21
- method_matched && no_args || args_match
22
- end
23
-
24
- if matching
25
- true
26
- else
27
- false
28
- end
8
+ @invocations_of_method = subject.invocations.select {|i| i[:method] == @call_to_verify}
9
+ verifying_args = @args_to_verify.size != 0
10
+
11
+ match_passes?(verifying_args)
29
12
  end
30
13
 
31
14
  def failure_message_for_should_not
@@ -34,9 +17,9 @@ class Debriefing
34
17
 
35
18
  def failure_message_for_should
36
19
  if @args_to_verify.size > 0
37
- "Spy(:#{@subject.name}) expected to receive :#{@call_to_verify}(#{@args_to_verify.map(&:inspect).join(", ")}) #{prettify_times(@expected_calls)}, received #{prettify_times(@matching_calls)}"
20
+ "Spy(:#{@subject.name}) expected to receive :#{@call_to_verify}(#{@args_to_verify.map(&:inspect).join(", ")}) #{prettify_times(@expected_call_count)}, received #{prettify_times(@matching_calls)}"
38
21
  else
39
- "Spy(:#{@subject.name}) expected to receive :#{@call_to_verify} #{prettify_times(@expected_calls)}, received #{prettify_times(@matching_calls)}"
22
+ "Spy(:#{@subject.name}) expected to receive :#{@call_to_verify} #{prettify_times(@expected_call_count)}, received #{prettify_times(@matching_calls)}"
40
23
  end
41
24
  end
42
25
 
@@ -59,4 +42,22 @@ class Debriefing
59
42
  end
60
43
  end
61
44
  private :prettify_times
45
+
46
+ def matching_calls(verifying_args=true)
47
+ @matching_calls ||= if verifying_args
48
+ @invocations_of_method.select {|i| i[:args].flatten === @args_to_verify}.size
49
+ else
50
+ @invocations_of_method.size
51
+ end
52
+ end
53
+ private :matching_calls
54
+
55
+ def match_passes?(verifying_args)
56
+ if @expected_call_count
57
+ matching_calls(verifying_args) == @expected_call_count
58
+ else
59
+ matching_calls(verifying_args) > 0
60
+ end
61
+ end
62
+ private :match_passes?
62
63
  end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: matahari
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 31
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
7
8
  - 1
8
- - 1
9
- version: 0.1.1
9
+ - 2
10
+ version: 0.1.2
10
11
  platform: ruby
11
12
  authors:
12
13
  - Tom Stuart
@@ -25,6 +26,7 @@ dependencies:
25
26
  requirements:
26
27
  - - ">="
27
28
  - !ruby/object:Gem::Version
29
+ hash: 3
28
30
  segments:
29
31
  - 0
30
32
  version: "0"
@@ -38,6 +40,7 @@ dependencies:
38
40
  requirements:
39
41
  - - ">="
40
42
  - !ruby/object:Gem::Version
43
+ hash: 3
41
44
  segments:
42
45
  - 0
43
46
  version: "0"
@@ -76,6 +79,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
76
79
  requirements:
77
80
  - - ">="
78
81
  - !ruby/object:Gem::Version
82
+ hash: 3
79
83
  segments:
80
84
  - 0
81
85
  version: "0"
@@ -84,6 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
88
  requirements:
85
89
  - - ">="
86
90
  - !ruby/object:Gem::Version
91
+ hash: 3
87
92
  segments:
88
93
  - 0
89
94
  version: "0"