Reputation: 2112
Hello I have something like the following code inside a rails model:
class Page < ActiveRecord::Base
def calculate_page_speed(url)
browser = Watir::Browser.new :phantomjs
start = Time.now
browser.goto url
finish = Time.now
self.speed = finish - start
end
end
Here is the test:
describe Page do
context "calculate_page_speed for Page" do
let(:google) { FactoryGirl.create(:page, url: "http://www.google.com") }
it "should set the page speed" do
google.speed.should be_nil
google.calculate_page_speed
google.speed.should_not be_nil
end
end
end
How can I stub effectively the external service for not calling it during the test?
Upvotes: 1
Views: 699
Reputation: 115541
There is an approach like:
class Page < ActiveRecord::Base
def calculate_page_speed(url)
start = Time.now
browser.goto url
finish = Time.now
self.speed = finish - start
end
def browser
@browser ||= Rails.env.test? ? FakeBrowser.new : Watir.new(:phantomjs)
end
class FakeBrowser
def goto
end
end
end
but here you have test code in prod code which is not great.
A derived solution would be to inject the expected browser class in a config.
Finally you can just stub stuff:
Watir::Browser.any_instance.stub(:goto)
Upvotes: 2
Reputation: 50057
I do not think that visiting a url and measuring the speed
belongs inside the Page
model, setting the speed is, but not the actual calculation. So I would
create a class in lib
that allows me to measure the speed of a url, and use that class instead. I would allow that class to be injected with a stub.
So something like this, in lib\page_visitor.rb
:
class PageVisitor
def initialize(browser = Watir::Browser.new(:phantomjs))
@browser = browser
end
def measure_speed(url)
start = Time.now
@browser.goto url
finish = Time.now
finish - start
end
end
And the your Page
becomes:
def calculate_page_speed(url)
self.speed = PageVisitor.new.measure_speed(url)
end
So with that setup, you can simplify your tests: in Page
you have to check the right function is called, and likewise, in PageVisitor
check that goto
is called, without actually going to the page.
So, in spec/models/page_spec.rb
write:
let(:google) { FactoryGirl.create(:page, url: "http://www.google.com") }
it("initial speed is zero") { google.speed.should be_nil }
describe `calculating the speed` do
before do
PageVisitor.any_instance.should_receive(:measure_speed).and_return(25)
google.calculate_page_speed
end
it "sets the speed correctly"
google.speed.should == 25
end
end
Using any_instance
could be considered a smell, but in this case I think it is the easiest way to test it. If you really want to avoid it, instead of changing the code (I think the code is ok, no need to inject a PageVisitor
imho), you could stub PageVisitor.new
to return a specific PageVisitor
, and then you can just stub measure_speed
on that instance.
In your spec/lib/page_visitor_spec.rb
write
describe PageVisitor
class FakeBrowser
def goto(url)
sleep 1
end
end
let(:page_visitor) { PageVisitor.new(FakeBrowser.new) }
it "measure the speed" do
page_visitor.measure_speed.should_not be_nil
end
end
This should get you started.
Upvotes: 1