How to mock a class with Internet access, faking time-dependent content changes

31 views
Skip to first unread message

JSchlinker

unread,
Jun 26, 2015, 6:02:37 AM6/26/15
to rs...@googlegroups.com
I am using Rspec to test a module that has an external dependency with the Internet.
This is why I am trying to mock out the class that handles the actual access of the webpage when running my tests.

My class "Snapshot" (that I want to test) accesses a webpage when it is requested and will save the html in a file when the hash of the content has changed.
The filename should have a certain name according to the date/time and a running number.
And there should be a log message when an error occours (e.g. no Internet or website down), containing the status code (e.g. 404).

I made some basic tests, but now wanted to write a test integrating a couple of classes.
The only thing that I would like to mock is the Internet part.
As the snapshots are only generated when the html content changes I thought about the following approach:

- Use timecop to fake a time and be able to check if the saved filename corresponds to this date/time
- Mock the class "Internet" by specifying certain time spans in which the class should return a certain content

My class under test looks like this:

class Snapshot < ActiveRecord::Base

     
def self.grab_website(uri)            
         response
, content = Internet.access_webpage(uri)
         
# code that calculates the hash, saves the content or generates an error log messages
     
end

end


My Rspec "preparation code" for mocking the class Internet looks like this:  

stub_const("Internet::WEBSITE_REPLIES", \
       
{ "http://www.thiswebsiteisimportanttome.com" => \
               
[["2015-06-12 15:30", "website_Version_A.html"] ,\
                 
["2015-06-14 18:00", "website_Version_B.html"] ,\
                 
["2015-06-14 18:50", 404] ,\
                 
["2015-06-14 19:00", "website_Version_C.html"] ,\
                 
["2015-06-26 11:35", "website_Version_D.html"]] } )

 allow
(Internet).to receive(:access_webpage) do |uri|
    time
= Time.now
    content
= nil
    response_code
= 0               # We're not connected to the internet

   
if Internet::WEBSITE_REPLIES.has_key?(uri)
        filename
= nil
        timetable
= Internet::WEBSITE_REPLIES[uri]
        timetable
.each do |timepoint_situation|
            timepoint
= Time.parse(timepoint_situation[0])
           
if time > timepoint
                filename
= timepoint_situation[1]
           
end
       
end
       
if (filename.nil?)
            response_code
= 0
       
elsif (filename.kind_of? Integer)
            response_code
= filename
       
else # filename is really a filename
            content
= File.read(filename)
            response_code
= 200
       
end
   
end
   
next response_code, content
end


And my actual test looks like this:

first_time = Time.local(2015, 6, 12, 15, 35)
second_time
= Time.local(2015, 6, 14, 17, 00)
third_time
= Time.local(2015, 6, 14, 18, 05)

uri
= "http://www.thiswebsiteisimportanttome.com"
Timecop.freeze(first_time) # Version A
Snapshot.grab_website(uri)        

Timecop.freeze(second_time) # Version A (no changes)
Snapshot.grab_website(uri)        

Timecop.freeze(third_time) # Version B
Snapshot.grab_website(uri)        

snapshots
= Snapshot.all
expect
(snapshots.size).to eq(2)

snapshot
= snapshots[0]
expect
(snapshot.time).to eq(first_time)
expect
(snapshot.hash).to eq("234092384EF")
expect
(snapshot.filename).to eq(...)

snapshot
= snapshots[1]
expect
(snapshot.time).to eq(third_time)
expect
(snapshot.hash).to eq("A8E92340213")
expect
(snapshot.filename).to eq(...)

# work to do: check the log messages ...


I have the feeling that there should be an easier way to do this.
Especially because I can't imagine that I'm the only one that wants to test time dependent Website content.
I am grateful for any suggestions!

J.

Myron Marston

unread,
Jun 26, 2015, 11:10:36 AM6/26/15
to rs...@googlegroups.com

--
You received this message because you are subscribed to the Google Groups "rspec" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rspec+un...@googlegroups.com.
To post to this group, send email to rs...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/rspec/c1ed74fa-8f54-40ae-a6ba-ccfab2584bc1%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Take a look at VCR. It is specifically designed for when you are testing HTTP-dependent code.


Juergen Schlinker

unread,
Jun 29, 2015, 3:34:17 AM6/29/15
to rs...@googlegroups.com
I already had a quick look at VCR before.
I always thought that it's best for replaying real http content.
I am looking at faking web content dependent on the time.
However, I might be able to "bend" VCR to suite my needs.
Thanks for the suggestion!


--
You received this message because you are subscribed to a topic in the Google Groups "rspec" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/rspec/dqkIqrX5Vgo/unsubscribe.
To unsubscribe from this group and all its topics, send an email to rspec+un...@googlegroups.com.

To post to this group, send email to rs...@googlegroups.com.

Myron Marston

unread,
Jun 29, 2015, 3:36:10 AM6/29/15
to rs...@googlegroups.com
If you're not trying to record and replay real content, VCR is probably not the right tool (thought it can be "bent" to fit your use case, as you say).  I'd suggest that WebMock might fit your use case better:


Reply all
Reply to author
Forward
0 new messages