README in rr-0.3.11 vs README in rr-0.4.0
- old
+ new
@@ -1,95 +1,186 @@
= RR
-RR (Double Ruby) is a double framework that features a rich
+RR (Double Ruby) is a test double framework that features a rich
selection of double techniques and a terse syntax.
+
+== What is a Test Double?
+A Test Double is a generalization of something that replaces a real
+object to make it easier to test another object. Its like a stunt
+double for tests. The following are test doubles:
+* Mocks
+* Stubs
+* Fakes
+* Spies
+* Proxies
http://xunitpatterns.com/Test%20Double.html
-Currently RR implements mocks, stubs, and probes. It is a goal of
-RR to support a wide range of double techniques and patterns.
+Currently RR implements mocks, stubs, and proxies. In the future, RR will
+support spies.
-== Mocks
-http://xunitpatterns.com/Mock%20Object.html
+== Syntax between RR and other double/mock frameworks
+=== Terse Syntax
+One of the goals of RR is to make doubles more scannable.
+This is accomplished by removing words from a double declaration.
+Here is RR compared to other mock frameworks:
+
+ flexmock(User).should_receive(:find).with('42').and_return(jane) # Flexmock
+ User.should_receive(:find).with('42').and_return(jane) # Rspec
+ User.expects(:find).with('42').returns {jane} # Mocha
+ User.should_receive(:find).with('42') {jane} # Rspec using return value blocks
+ mock(User).find('42') {jane} # RR
+
+=== No mock objects
+RR is an opinionated framework. RR does not create a mock object for you,
+like other frameworks. Instead, RR utilizes a technique known as
+"partial mocking".
+
+ my_object = MyClass.new
+ mock(my_object).hello
+
+Compare this with doing a mock in mocha:
+ my_mocked_object = mock()
+ my_mocked_object.expects(:hello)
+
+=== No should_receive or expects method
+RR uses method_missing to set your method expectation. This means you do not
+need to use a method such as should_receive or expects.
+
+ mock(my_object).hello # The hello method on my_object is mocked
+
+Mocha:
+ my_object.expects(:hello) # expects sets the hello method expectation
+Rspec mocks:
+ my_object.should_receive(:hello) # should_receive sets the hello method expectation
+
+=== with method call is not necessary
+Since RR uses method_missing, it also make using the with method unnecessary
+to set the argument expectations.
+
+ mock(my_object).hello('bob', 'jane')
+
+Mocha:
+ my_object.expects(:hello).with('bob', 'jane')
+Rspec mocks:
+ my_object.should_receive(:hello).with('bob', 'jane')
+
+=== using a block to set the return value
+RR supports using a block to set the return value. RR also has the #returns method.
+Both of the examples are equivalent.
+
+ mock(my_object).hello('bob', 'jane') {'Hello Bob and Jane'}
+ mock(my_object).hello('bob', 'jane').returns('Hello Bob and Jane')
+
+Mocha:
+ my_object.expects(:hello).with('bob', 'jane').returns('Hello Bob and Jane')
+Rspec mocks:
+ my_object.should_receive(:hello).with('bob', 'jane').and_return('Hello Bob and Jane')
+ my_object.should_receive(:hello).with('bob', 'jane') {'Hello Bob and Jane'} #rspec also supports blocks for the return value
+
+== Using RR
+To create a double on an object, you can use the following methods:
+* mock
+* stub
+* proxy
+* instance_of
+
+These methods are composable. mock and stub can be used by themselves and
+are mutually exclusive.
+proxy and instance_of must be chained with mock or stub. You can chain
+proxy and instance_of together.
+
+=== mock
+mock replaces the method on the object with an expectation and implementation.
+The expectations are a mock will be called with certain arguments a certain
+number of times (the default is once). You can also set the return value
+of the method invocation.
+
+See http://xunitpatterns.com/Mock%20Object.html
+
+The following example sets an expectation that the view will receive a method
+call to #render with the arguments {:partial => "user_info"} once.
+When the method is called "Information" is returned.
view = controller.template
mock(view).render(:partial => "user_info") {"Information"}
- # or
- mock(view) do
- render(:partial => "user_info") {"Information"}
- end
+=== stub
+stub replaces the method on the object with only an implementation. You
+can still use arguments to differentiate which stub gets invoked.
-== Stubs
-http://xunitpatterns.com/Test%20Stub.html
+See http://xunitpatterns.com/Test%20Stub.html
+
+The following example makes the User.find method return jane when passed
+'42' and returns bob when passed '99'. If another id is passed to User.find,
+an exception is raised.
jane = User.new
+ bob = User.new
stub(User).find('42') {jane}
+ stub(User).find('99') {bob}
+ stub(User).find do |id|
+ raise "Unexpected id #{id.inspect} passed to me"
+ end
-== Mock Proxies/Probes
-Add verifications that a method was called while actually calling it.
-The following example verifies render partial will be called and
-renders the partial.
+=== mock.proxy
+mock.proxy replaces the method on the object with an expectation, implementation, and
+also invokes the actual method. mock.proxy also intercepts the return value and
+passes it into the return value block.
+The following example makes sets an expectation that view.render({:partial => "right_navigation"})
+gets called once and return the actual content of the rendered partial template.
+A call to view.render({:partial => "user_info"}) will render the user_info
+partial template and send the content into the block and is represented by the html variable.
+An assertion is done on the html and "Different html" is returned.
view = controller.template
mock.proxy(view).render(:partial => "right_navigation")
mock.proxy(view).render(:partial => "user_info") do |html|
html.should include("John Doe")
"Different html"
end
-Probes support after_call callbacks. This is useful for Stubbing out
-a class method and getting its return value. You can also change the return value.
-This technique is also useful for verifying that you are mocking exists and
-functions proberly, thereby testing you interface.
-For example, using ActiveRecord:
-
- mock.proxy(User).find('5') do |user|
- mock.proxy(user).projects do |projects|
+You can also use mock.proxy to set expectations on the returned value. In
+the following example, a call to User.find('5') does the normal ActiveRecord
+implementation and passes the actual value, represented by the variable bob,
+into the block. bob is then set with a mock.proxy for projects to return
+only the first 3 projects. bob is also mocked with valid? to return false.
+ mock.proxy(User).find('5') do |bob|
+ mock.proxy(bob).projects do |projects|
projects[0..3]
end
- mock(user).valid? {false}
- user
+ mock(bob).valid? {false}
+ bob
end
-== Stub Proxy/Probes
+=== stub.proxy
Intercept the return value of a method call.
The following example verifies render partial will be called and
renders the partial.
view = controller.template
stub.proxy(view).render(:partial => "user_info") do |html|
html.should include("Joe Smith")
html
end
-== Instance of Doubles
+=== instance_of
Put double scenarios on instances of a Class.
mock.instance_of(User).valid? {false}
-== Block Syntax
+=== Block Syntax
script = MyScript.new
mock(script) do |m|
m.system("cd #{RAILS_ENV}") {true}
m.system("rake foo:bar") {true}
m.system("rake baz") {true}
end
-== Terse Syntax
-One of the goals of RR is to make doubles more scannable.
-This is accomplished by removing words from a double declaration.
-Here is RR compared to other mock frameworks:
-
- flexmock(User).should_receive(:find).with('42').and_return(jane) # Flexmock
- User.should_receive(:find).with('42').and_return(jane) # Rspec
- User.expects(:find).with('42').returns {jane} # Mocha
- User.should_receive(:find).with('42') {jane} # Rspec using return value blocks
- mock(User).find('42') {jane} # RR
-
== Special Thanks To
With any development effort, there are countless people who have contributed
to making it possible. We all are standing on the shoulders of giants.
* Pivotal Labs for sponsoring RR development
* Parker Thompson for pairing with me
* Felix Morio for pairing with me
+* Jeff Whitmire for documentation suggestions
* David Chelimsky for encouragement to make the RR framework, for developing
the Rspec mock framework, and syntax ideas
* Gerald Meszaros for his excellent book "xUnit Test Patterns"
* Dan North for syntax ideas
* Jim Weirich for developing Flexmock, the first Terse ruby mock framework