Michael,
Thanks for the link to Mocha, it looks like what I need. However, I
still don't understand what to do to test live data. This is my project:
http://github.com/michaelboutros/rsnipt/tree/master. How would you go at
what I'm trying to do?
Thanks,
Michael Boutros
I am of the opinion that when testing external services you should not
actually be hitting that external resource during the test. As I said
before the way I handle this is to capture the response and store it
is as a fixture and then mock/stub the appropriate method.
In your library's particular case, I would start with your Snipt#login
method. First you may need to break your methods up into smaller
chunks in order to mock/stub the appropriate piece of the method. I
should also mention that I've never done any of this type of testing
with Mechanize before.
Disclaimer: I'm sure someone else can point out a better way to do
this. This is also largely untested...
The first line of your Snipt#login method:
login_form = @agent.get('
http://www.snipt.net/login').forms.first
I would break this up into two new methods:
class Snipt
def login_page
agent.get('
http://www.snipt.net/login')
end
def login_form
login_page.forms.first
end
end
By doing this, it allows you to mock the Snipt#login_page method.
Moving along, I now think to myself, what do I need to do to make
Snipt#login_page return the type of object that I am expecting? I
know that in this case, @agent.get is going to return a
WWW::Mechanize:

age object. Not only that, but that it is going to
use the response from
http://www.snipt.net/login in order to construct
this object.
The next step is to capture the response of
http://www.snipt.net/login
in a test fixture.
require 'open-uri'
File.open('test/fixtures/login.html', 'w') do |f|
f.write open('
http://www.snipt.net/login').read
end
Now this makes the assumption that the login page is never going to
change, which is reasonable since your library is only made to work
with this particular version of the form. If something should change
on snipt.net/login you could refresh the fixture with the same code as
above and run your tests to make sure nothing is broken. If you're
testing against a fast moving target it may be a good idea to put the
above snippet in a Rake task so that you can easily refresh all of
your test fixtures.
Then I would look into constructing a WWW::Mechanize:

age object from
the fixture that you have saved in your test fixture directory and
setting up the mock to work properly. To do this I would create a
WWW::Mechanize:

age object and use that as the return for your mock.
As I reached this point in writing the response I noticed another
thing that I would do in order to make testing easier. I'd like to
reiterate that this is all just a matter of opinion, but I cannot see
any other way to do this.
I would make your constructor simply return the Snipt object and allow
another method to handle logging in.
class Snipt
def initialize(username, password)
@detailed_return = false
@username, @password = username, password
@logged_in = false
@lexers = {}
end
def self.login
snipt = Snipt.new(username, password)
snipt.login
snipt
end
def agent
@agent ||= WWW::Mechanize.new
@agent.user_agent_alias = 'Mac FireFox'
@agent
end
end
This gives you the ability to construct a Snipt object that is not
logged in for easier testing and gives you a class method for
convenience of creating a logged in Snipt object. I also moved the
construction of the Mechanize::Agent object into it's own method for
easier testing/mocking.
require 'snipt'
require 'test/unit'
require 'flexmock/test_unit'
class TestSniptLogin < Test::Unit::TestCase
def test_login_should_be_false_when_unsuccessful
snipt = Snipt.new('foo', 'bar')
login_page = WWW::Mechanize:

age.new(nil, { 'content-type' =>
'text/html' }, open('test/fixtures/login.html').read, 200)
flexmock(snipt.agent) do |mock|
mock.should_receive

get).and_return(login_page)
end
# THIS IS INCOMPLETE
# You will also need to mock the submission of the login_form in
your Snipt#login method.
# You will have to create a fixture representing the failed login
form for this case.
# For the case of a successful login, you will want to create a
fixture representing the response
# during success.
assert ! snipt.login
end
end
Again, this code is largely untested, but it should give you more than
enough information to get started. Originally I was just going to
fork your project and submit a pull request with some changes, but I
decided it would probably be better to give an explanation and some
thoughts along the way.
Happy Holidays,
Michael Guterl