Brian Takita's blog
RR 0.6.2 Released
This release features a sexier Double definition block syntax using instance_eval:
mock(User) do
find(1) {user_1}
find(2) {user_2}
end
RR also maintains its non-instance_evaled Double definition block syntax, if the block has an arity of 1:
mock(User) do |expect|
expect.find(1) {user_1}
expect.find(2) {user_2}
end
The instance_eval syntax is very useful in the context of Double Definition chaining:
mock.proxy(User).find(1) do
mock.proxy! do
blogs do
mock.proxy!.find_by_id(2) do |actual_blog|
actual_blog.name.should == "My ranty blog"
actual_blog
end
end
fans do
mock.proxy!.thank_everybody
end
foes do
mock.proxy!.ask_for_forgiveness
end
end
end
user = User.find(1)
user.blogs.find_by_id(2) # My ranty blog
user.fans.thank_everybody
user.foes.ask_for_forgiveness
Of course the previous example is a complicated case of interaction testing, and a simpler state-based and/or hybrid approach may be better, but it demonstrates how using instance_eval can promote readability.
Here is the non-instance_eval solution:
mock.proxy(User).find(1) do
mock.proxy! do |expect|
expect.blogs do
mock.proxy!.find_by_id(2) do |actual_blog|
actual_blog.name.should == "My ranty blog"
actual_blog
end
end
expect.fans do
mock.proxy!.thank_everybody
end
expect.foes do
mock.proxy!.ask_for_forgiveness
end
end
end
instance_eval Controversy
Ola Bini warned against overusing instance_eval. While instance_eval adds beauty, it can also make things more difficult to extend and debug.
In RR, the DoubleDefinitionCreatorProxy (the object that is instance_evaled when defining Doubles using blocks) uses the blank slate pattern, so arbitrary method names can be passed in to define the Double. The Blank Slate implies that the DoubleDefinitionCreatorProxy will not be extended with methods. James Earl Gray explains the pattern that I used for RR.
spec/test helper methods will not be usable within the instance_evaled blocks because the intent of the DoubleDefinitionCreatorProxy object is incompatible with the instance_eval with delegation pattern. DoubleDefinitionCreatorProxy already uses method_missing to create DoubleDefinitions. If you wish to use spec/test helper methods, you will need to memoize it to a variable and use lexical scoping.
Now, one may want to define a helper method in the test/spec that is returned when the Double is invoked.
describe User do
describe "#fans.thank_everybody" do
it "thanks all of my lovely fans" do
user = User.new
memoized_my_lovely_fans = my_lovely_fans
mock(user) {fans {memoized_my_lovely_fans}}
memoized_my_lovely_fans.each {|fan| mock(fan).thank_you}
user.fans.thank_everybody
end
def my_lovely_fans
[Fan.new, Fan.new]
end
end
end
While I think this will be a good addition to RR, I recognize that adding the instance_eval has the possibility of making RR less usable. I'll pay close attention to see how this pans out and am willing to remove it if there are major issues.
Effective Markdown Editing with the WMD editor and the Save Text Area Firefox plugin
Anytime I need to edit Markdown, I reach for the WMD editor. Their splitscreen demo is the most effective way to edit Markdown that I have seen.
The left screen is the editor and the right screen is the "real-time" preview of the Markdown. It is nice because I don't have to press a preview button to see rendered Markdown. The Markdown is also rendered as I type so I get instantaneous feedback of my changes.
There is also a Save Text Area Firefox addon, which enables me to save/load the contents of a text area to/from a file on the filesystem. Also the Ctrl+s shortcut saves the file.
So when editing Markdown, I:
- Open Firefox and go to http://wmd-editor.com/examples/splitscreen?blank=1
- Load or Save a markdown file by right-clicking the editor screen
- Going to the Text submenu
- Clicking Load or Save As
- Edit the file and see the generated output
Of course, its not a text editor replacement, since the possible text manipulation in Firefox is limited, but the feedback that is provided by WMD is very effective to rapid Markdown editing. I hope this sort of UI becomes more common.
Now if only there were a similar Textile editor...
NYC Stand Up 10/15 - 10/16 2008
10/15/2008
Help!
Autocompleters - Which one do we use?
We are currently using the YUI autocompleter from YUI 2.4.1 which works well. However, we are making significant architectural changes, and the "new" way used the YUI autocomplenet from 2.6.0. We don't want to install YUI just for the autocompleter and we don't want to deal with the version mismatch, so we are going to use a different autocompleter. It seems that there are many autocompleters, so which one do we use?
NOTE: We ended up going with the jQuery autocompleter. The autocompleter had issues with noConflict mode and with pressing the backspace key. We fixed them and will post the fixed version to Github soon.
Interesting
- Postgres replication requires a primary key, and HABTM solutions typically do not use primary keys. Has many through is better anyway.
10/16/2008
Help!
CI build is slow and we don't know why
Our CI build is failing because of the performance issues. Some strange things we noticed:
There are many Sockets in the TIME_WAIT state
A possible solution is to run the OSX diskutils on the CI box.
Piston Hangs when importing a SVN dependency into a Git repo
Interesting
The Heavy team is coming for lunch to discuss process
git-svn is difficult to work with
- Use all Git or all SVN if you can.
Polonium
- Using click_and_wait fixes failures due to timeout on the CI box. It is more performant because click_and_wait is implemented in JS. The standard Polonium wait_for assertions poll into SeleniumRC.
NYC Stand Up 10/14 2008
A developer accidentally committed to an SVN external. Switching the SVN user to have read-only access to the external.
Casebook's ccrb + Funkytown + JsUnit build opens parallels on development boxes. The issue is probably with the Funkytown configuration.
Adam Keyes is coming in to talk to us during lunch today.
Casebook is moving to Git.
NYC Stand Up 10/13 2008
Interesting
"All your brunch is belong to us" - Damon McCormick
NYC Ruby Tomorrow
Erector 0.4.200 was released. New features include:
The Widget#join method, which joins an Array of text/widgets, with a separator which is text or a widget. This translates the standard idiom, without needing calls to raw.
join [ "People", "Projects" ], " | "
Added a Pretty Printer which can be enabled by Erector::Doc.prettyprint_default = true (for example in development.rb)
JsUnit runs very slowly on the CI box on OSX and eventually times out, unless we open a VNC window to the machine. We tried upgrading to a more recent JsUnit, but couldn't get that to work at all.
Nanite looks really cool.
RR 0.6.0 Released
I'm pleased to announce the 0.6.0 version of RR. The changes include:
- Declaring Double subject objects without having to pass it in via the mock!, stub!, dont_allow!, instance_of!, and proxy! methods
- Revised Double chaining API
- satisfy matcher
- hash_including matcher
Declaring Double Subjects (The bang methods)
In previous versions of RR, you always needed to pass in the subject of the double. For example:
subject = Object.new
mock(subject).does_something {:and_returns_me}
subject.does_something # :and_returns_me
Now you can have RR automatically create the subject object for you by using the ! method:
subject = mock!.does_something {:and_returns_me}.subject
subject.does_something # :and_returns_me
Now the bang methods by themselves don't really add a whole lot, but when used in the context of Double chaining, they become a powerful addition.
Double Chaining
Nick Kallen presented the use case for Double chaining and contributed a patch for the 0.5.0 release of RR. It has proved useful and is now more fully incorporated into RR. Now you can pass in your subject or use the subject provided by RR by using the ! method. Here are some examples of Double Chaining:
mock(subject).first(1) {mock(Object.new).second(2) {mock(Object.new).third(3) {4}}}
subject.first(1).second(2).third(3) # 4
mock(subject).first(1) {mock!.second(2) {mock!.third(3) {4}}}
subject.first(1).second(2).third(3) # 4
mock(subject).first(1).mock!.second(2).mock!.third(3) {4}
subject.first(1).second(2).third(3) # 4
Of course you have access to the proxy facilities:
mock.proxy(User).find('1').mock.proxy!.children.mock.proxy!.find_all_by_group_id(10)
User.find('1').children.find_all_by_group_id(10) # Makes verifications pass and returns the actual children
You can also do branched Double chaining:
mock(subject).first do
mock! do |expect|
expect.branch1.mock!.branch11 {11} # or expect.branch1 {mock!.branch11 {11}}
expect.branch2.mock!.branch22 {22} # or expect.branch2 {mock!.branch22 {22}}
end
end
o = subject.first
o.branch1.branch11 # 11
o.branch2.branch22 # 22
Satisfy Matcher
Matthew O'Conner submitted a patch that added the satisfy matcher. This adds the ability to add arbitrary argument expectation matchers.
mock(object).foobar(satisfy {|arg| arg.length == 2})
object.foobar("xy")
Hash Including Matcher
Matthew O'Conner also submitted a patch that added the hash_including matcher. This adds a convenient way to assert that the passed-in hash includes certain key/value pairs.
mock(object).foobar(hash_including(:red => "#FF0000", :blue => "#0000FF"))
object.foobar({:red => "#FF0000", :blue => "#0000FF", :green => "#00FF00"})
Mailing list
RR has a mailing lists at:
Also, RR's rubyforge page is at http://rubyforge.org/projects/double-ruby and of course the github page is at http://github.com/btakita/rr.
Yes, and there is more to come
There are many interesting ideas floating around. Joseph Wilk has been playing around with adding Spies into RR. I'm also thinking about adding Double validation scoping into RR. Also, I'm impressed by Mocha's warning of unused stubs. Josh Susser also proposed having a mode where a warning would occur if a mocked method is not implemented on the subject being mocked.
If you have any feature requests, please send an email to the mailing list or add it to the rubyforge tracker.
Build Your Own Rails Plugin Platform with Desert
While it is easy to include plugins in your Rails projects, it isn't easy to extend and customize the plugin for your own application. Desert solves that limitation/complication by making it just as easy to extend or modify a plugin class as it would be with any other class. In this post we will go over how Desert provides an easy way to manage and extend your plugins.
At Pivotal, we offer an integrated platform of Rails plugins named Socialitis.
Socialitis is an internal project that grew out of the observation that many of our start-up clients needed to build the same non-differentiating features; user management, friends/contacts, activity feeds, on-site messaging, etc.
The Socialitis platform is broken up into number of plugins that extend the Rails app in specific ways. These plugins may have dependencies on other plugins.
One of the major design goals of Socialitis is easy, drop-in, integration into existing Rails apps. This means using convention over configuration and removing as much integration responsibility from the user of the plugin as possible.
Another design goal was to provide sensible defaults and make each plugin easy to customize for your app.
We used Desert to achieve these goals, and so can you for your own platform.
The major features that Desert provides are:
- Defining a Rail's like directory structure into your plugin (models, views, controllers, helpers)
- Plugin dependencies
- Seamless overriding of classes and modules defined by parent plugins
- Plugin migrations
- Plugin routing
Desert provides a similar feature set to the Radient plugin system and the now defunct Appable Plugins framework.
For a simple example, lets say you have two plugins, name User and Messaging. The User plugin provides basic authentication and login features, and the Messaging plugin allows Users to send Messages to each other. The Message plugin depends on the User plugin.
The directory structure of the full Rails app looks like:
|-- app
| |-- controllers
| | |-- application.rb
| | `-- blogs_controller.rb
| |-- helpers
| | |-- application_helper.rb
| | `-- blogs_helper.rb
| |-- models
| | `-- user.rb
| `-- views
| |-- blogs
| |-- layouts
| | `-- users.html.erb
| `-- users
| |-- index.html.erb
| `-- show.html.erb
|-- db
| `-- migrate
| `-- 001_migrate_users_to_001.rb
|-- lib
| `-- current_user.rb
|-- spec
| |-- controllers
| | `-- blogs_controller_spec.rb
| |-- fixtures
| |-- models
| |-- spec_helper.rb
| `-- views
| `-- blogs
`-- vendor
`-- plugins
`-- user
|-- app
| |-- controllers
| | |-- logins_controller.rb
| | `-- users_controller.rb
| |-- helpers
| | |-- logins_helper.rb
| | `-- users_helper.rb
| |-- models
| | |-- login.rb
| | `-- user.rb
| `-- views
| |-- logins
| | |-- edit.html.erb
| | |-- index.html.erb
| | |-- new.html.erb
| | `-- show.html.erb
| `-- users
| |-- edit.html.erb
| |-- index.html.erb
| |-- new.html.erb
| `-- show.html.erb
|-- config
| `-- routes.rb
|-- db
| `-- migrate
| `-- 001_create_users.rb
|-- init.rb
|-- lib
| `-- current_user.rb
|-- spec
| |-- controllers
| | `-- user_controller_spec.rb
| |-- fixtures
| | `-- users.yml
| |-- models
| | `-- user.rb
| |-- spec_helper.rb
| `-- views
| `-- users
`-- tasks
`-- message
|-- app
| |-- controllers
| | `-- message_controller.rb
| |-- helpers
| | |-- message_helper.rb
| | `-- user_helper.rb
| |-- models
| | |-- message.rb
| | `-- user.rb
| `-- views
| `-- messages
| |-- edit.html.erb
| |-- index.html.erb
| |-- new.html.erb
| `-- show.html.erb
|-- config
| `-- routes.rb
|-- db
| `-- migrate
| `-- 001_create_messages.rb
|-- init.rb
|-- spec
| |-- controllers
| | |-- message_controller_spec.rb
| | `-- user_controller_spec.rb
| |-- fixtures
| | |-- messages.yml
| | `-- users.yml
| |-- models
| | |-- message_spec.rb
| | `-- user_spec.rb
| |-- spec_helper.rb
| `-- views
| `-- messages
`-- tasks
The User plugin introduces the various User and Login Rails objects. The Message plugin introduces its respective Message objects. Notice that the Message plugin also reopens some of the User objects to insert functionality.
For example, vendor/plugins/users/app/models/user.rb looks something like:
class User < ActiveRecord::Base
has_many :logins
end
The Message plugin would then reopen User in vendor/plugins/message/app/models/user.rb:
class User < ActiveRecord::Base
has_many :messages_received
has_many :messages_sent
end
Meanwhile, the main application can also reopen User in app/models/user.rb
class User < ActiveRecord::Base
def custom_app_method
# custom app logic #
end
end
Desert allows you to utilize Ruby's ability to repoen classes to layer on functionality in your plugins and application. At Pivotal, we have had success in sharing code across multiple client applications using this technique.
Another thing to note is normally the Message plugin would be loaded before the User plugin. Desert allows you to create plugin dependencies. So in vendor/plugins/message/init.rb:
require_plugin 'user'
require_plugin 'will_paginate'
This means you no longer need to define plugin load order inside of environment.rb. Your plugins can take care of that. Desert works with practically all plugins. That means you can have a plugin dependency on any existing Rails plugin.
To see more examples & documentation, take a look at the Desert project at http://github.com/pivotal/desert.
jQuery & greasemonkey while being considerate to John's bandwidth
Here is a slight modification to Joan Piedra's jQuery greasemonkey script that loads the jQuery library on any page you visit. This gives you the power of jQuery when using Firebug or Greasemonkey. Here is an example of its usage.
var jQueryDefinition = (function() {/*jQuery library*/});
var GM_JQ = document.createElement('script');
GM_JQ.innerHTML = '(' + jQueryDefinition.toString() + ')();';
GM_JQ.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(GM_JQ);
Here is the full script. Here is the userscrips page.
Make sure that the jquery.user.js script is loaded before any other dependent Greasemonkey scripts.
The difference is you can put the jQuery library into the script itself. This causes the page to load faster and does not use up John Resig's bandwidth. Unfortunately, this also means you will need to manually upgrade your greasemonkey script when the next version of jQuery comes out.
[Update] Edited the Greasemonkey script to make jQuery compatible with other js libraries. [Update] Pointing to userscripts.org
Introducing RR
I'm pleased to introduce a new Test Double (or mock) framework named RR, which is short for Double Ruby.
Why a Double framework and not a Mock framework?
A mock is a type of test double. Since RR supports mocks, stubs, and proxies, it makes sense to refer to RR as a double framework. The proxy is a new usage pattern that I will introduce later in this article, and in more detail in future articles.
Unfortunately, the terminology over doubles has been contradictory depending on the framework. RR's terminology tries to be as faithful as possible to Gerald Meszaros' definition of test doubles. You can read more about test doubles in http://xunitpatterns.com/Test2e.html and Martin Fowler's article, http://martinfowler.com/articles/mocksArentStubs.html. Regretfully, this does mean that RR will have slightly different terminology than other double frameworks.
How does RR compare to other Mock frameworks?
Most double frameworks focus mainly on mocks (hence the categorization "mock framework"). RR's focus is on enabling more double test patterns in a terse and readable syntax.
RR also does not have dedicated mock objects. It primarily uses the technique called 'double injection'. Names that other frameworks use are 'stub injection', 'mock object injection', 'partial mocking', or 'stubbing'. The term I'll use for this is a double injection, since one or many doubles are being injected into an object's method.
I'll use trivial Rails examples to highlight the syntactical differences between RR, Mocha, Rspec's mocks, and Flexmock. They may or may not be appropriate situations for mocks. The right situations for mocks is an entirely different discussion.
If there is better way to do any of the examples, please post a comment and I will gladly replace it.
Mocks
Here are the ways to mock the User.find method. The expectation is the User class object will receive a call to #find with the argument '99' once and will return the object represented by the variable user.
RR
mock(User).find('99') { user }
Mocha
User.expects(:find).with('99').returns(user)
spec/mocks
User.should_receive(:find).with('99').and_return(user)
Flexmock
flexstub(User).should_receive(:find).with('99').and_return(user).once
Stubs
Here are the ways to stub the User.find method. When the User class object receives a call to find with the argument '99' it will return user1. When User receives find with any other arg, it returns user2.
RR
stub(User).find('99') { user1 }
stub(User).find { user2 }
Mocha
User.stubs(:find).with(anything).returns(2)
User.stubs(:find).with('99').returns(1)
spec/mocks
users = {
'99' => user1,
'default' => user2
}
User.stub!(:find).and_return do |id|
users[id] || users['default']
end
Flexmock
users = {
'99' => user1,
'default' => user2
}
flexstub(User).should_receive(:find).and_return do |id|
users[id] || users['default']
end
Proxy
A proxy used with a mock or stub causes the real method to be called. Expectations can be placed on the invocation and the return value can be intercepted. The main rationales are test clarity and you can ensure that the methods are being called correctly, even after you refactor your code. I will delve more into proxies and their usage patterns in my next article.
Mock Proxy
The following examples set an expectation that User.find('99') will be called once. The actual user is returned.
RR
mock.proxy(User).find('99')
Mocha
You cannot implement this in Mocha. You can do an approximation in this situation however. This technique is not always the solution you need, though.
user = User.find('99')
User.expects(:find).with('99').returns(user)
spec/mocks
find_method = User.method(:find)
User.should_receive(:find).with('99').and_return(&find_method)
Flexmock
find_method = User.method(:find)
User.should_receive(:find).with('99').and_return(&find_method)
Stub Proxy
The following examples intercept the return value of User.find('99') and stub out valid? to return false.
RR
stub.proxy(User).find('99') do |user|
stub(user).valid? {false}
user
end
Mocha
Again, this is an approximation, since you cannot use proxies in Mocha.
user = User.find('99')
user.stubs(:valid?).returns(false)
User.stubs(:find).with('99').returns(user)
spec/mocks
find_method = User.method(:find)
User.stub!(:find).with('99').and_return do |id|
user = find_method.call(id)
user.stub!(:valid?).and_return(false)
user
end
Flexmock
find_method = User.method(:find)
flexstub(User).should_receive(:find).with('99').and_return do |id|
user = find_method.call(id)
flexstub(user).should_receive(:valid?).and_return(false)
user
end
instance_of
instance_of is method sugar than allows you to mock or stub instances of a particular class. The following examples mock instances of User to expect valid? with no arguments to be called once and return false.
RR
mock.instance_of(User).valid? {false}
Mocha
User.any_instance.expects(:valid?).returns(false)
spec/mocks
new_method = User.method(:new)
User.stub!(:new).and_return do |*args|
user = new_method.call(*args)
user.should_receive(:valid?).and_return(false)
user
end
Flexmock
new_method = User.method(:new)
flexstub(User).should_receive(:new).and_return do |*args|
user = new_method.call(*args)
flexmock(user).should_receive(:valid?).and_return(false)
user
end
More to come
This concludes the introduction to RR. RR enables some techniques, like proxying, that will make your tests clearer and less brittle. In the next article I will describe into patterns and techniques that will make mocks a more feasible tool for more situations.
Column Edit Mode in VI
I've found that typing in column mode to be very useful when using editors like IntelliJ or TextMate. VI also has a column edit mode, that is a little tricky to use.
To use it, press:
- Ctrl + V to go into column mode
- Select the columns and rows where you want to enter your text
- Shift + i to go into insert mode in column mode
- Type in the text you want to enter. Dont be discouraged by the fact that only the first row is changed.
- Esc to apply your change (or alternately Ctrl+c)
You will now see your changed applied.







