UPDATE: Aslak noted that I'm not actually using mocks but stubs. The distinction appears to be in the use of expectations set on your mock object. Anyway, read more at [Martin Fowler's Bliki](http://www.martinfowler.com/articles/mocksArentStubs.html).
So the classic rails example: you're writing a blog. Maybe you've decided you want to be able to add feeds of your buddies Slash and Dingo to your sidebar so that their latest content shows up there.
You sit down to bang out code but because you're working to improve your code practices you decide to write tests first.
How do you write tests against information that might change or you don't always have access to in development? Stubbing to the rescue.
Stubs are a bit like fixtures outside of the database. We have some sort of information that our queries should return, so we stub those responses and are able to run our tests on our code without changing the application.
In our case, we're using something like this in our blog model to fetch our RSS feed and parse it.
1
2
3
4
5
6
7
8
9
10
|
def fetch
uri = URI.parse(self.rss_url)
http = Net::HTTP.new(uri.host,uri.port)
if !uri.query.nil?
rss = SimpleRSS.parse http.get(uri.path + '?' + uri.query, 'User-Agent' => 'glu.ttono.us/1.0').body
else
rss = SimpleRSS.parse http.get(uri.path, 'User-Agent' => 'glu.ttono.us/1.0').body
end
end
|
When we run tests the application shouldn't actually hit the website, so we'll stub out the methods that do that. In our case,
Net::HTTP.get (Note:
Net::HTTP.new doesn't actually make a connection).
What we'd like is to be able to have the request actually return information in a file that we've defined, so the call to
get should return the file and the call to
body should give us the contents.
Here's an easy way to implement that.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
require 'net/http'
module Net
class HTTP
def get(path, *args)
return File.open("#{RAILS_ROOT}/test/mocks/test/#{File.basename(path)}")
end
end
end
class File
alias_method :body, :read
end
|
Shove it in your
test/mocks/test directory, and write your test cases.
For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
require File.dirname(__FILE__) + '/../test_helper'
require File.dirname(__FILE__) + '/../mocks/test/http'
class BlogTest < Test::Unit::TestCase
fixtures :blogs, :items
def test_fetch
num_items = Item.count
b = blogs(:gluttonous_with_sample_feed)
assert_equal 0, b.items.count
b.fetch
assert_equal num_items + 10, Item.count
assert_equal 10, b.items.count
end
end
|
See Scott Baron's article [Mockery](http://scott.elitists.net/users/scott/posts/mockery) for information on Mocks (no, not stubs) in a more general Ruby sense.
January 12th, 2006 at 12:43 PM The fun part is you get to create your own User Agent!
January 12th, 2006 at 05:11 PM It's true!
January 13th, 2006 at 12:14 AM You're not mocking, but stubbing. http://www.martinfowler.com/articles/mocksArentStubs.html
January 21st, 2006 at 05:37 PM This has code smell to me. I wrote about my concerns on my blog: Why Rails bugs me, part 1:
test/mocks