开发者

How do I stub out the flickraw library in my app's unit tests?

开发者 https://www.devze.com 2023-01-12 19:12 出处:网络
My Rails 2 app displays a slideshow of photos from Flickr via the flickraw library.My code works, but I\'m stuck on how to properly write RSpec unit tests.

My Rails 2 app displays a slideshow of photos from Flickr via the flickraw library. My code works, but I'm stuck on how to properly write RSpec unit tests.

I have a Slide class that encapsulates everything that my app needs from flickraw. It acts somewhat like a model object but doesn't use ActiveRecord. It doesn't do much work; it delegates most of the heavy lifting to flickraw.

I haven't completed the tests because, as it is now, they require me to hard-code in some photo IDs from Flickr, and the tests would break if I rearranged my photoset or added new photos.

So my so-called unit tests are more l开发者_如何学Cike integration tests. I understand how to write a mock or stub using RSpec, but not sure how to do it to the flickraw library. How do I stub out flickraw and turn this into a unit test?

slide.rb:

require 'flickraw'
FlickRaw.api_key = "xxx"
FlickRaw.shared_secret = "yyy"
flickr.auth.checkToken :auth_token => "zzz"
PHOTOSET_ID = 123123123

class Slide
  attr_accessor :id, :previous_id, :next_id, :url_square, :url_thumbnail, :url_small, :url_medium500,
                :url_medium640, :url_large, :url_original

  def self.last
    photoset = flickr.photosets.getPhotos(:photoset_id => PHOTOSET_ID)
    Slide.new(photoset.photo.last.id)
  end

  def self.first
    photoset = flickr.photosets.getPhotos(:photoset_id => PHOTOSET_ID)
    Slide.new(photoset.photo.first.id)
  end

  def self.find(id)
    Slide.new(id)
  end

  def initialize(id)
    self.id = id
    photo = flickr.photos.getInfo(:photo_id => id)
    context = flickr.photosets.getContext(:photoset_id => PHOTOSET_ID, :photo_id => id)
    sizes = flickr.photos.getSizes(:photo_id => id)

    self.previous_id = (context.prevphoto.id == 0) ? nil : context.prevphoto.id
    self.next_id = (context.nextphoto.id == 0) ? nil : context.nextphoto.id

    sizes.each do |size|
      if size.label == "Square"
        self.url_square = size.source
      elsif size.label == "Thumbnail"
        self.url_thumbnail = size.source
      elsif size.label == "Small"
        self.url_small = size.source
      elsif size.label == "Medium"
        self.url_medium500 = size.source
      elsif size.label == "Medium 640"
        self.url_medium640 = size.source
      elsif size.label == "Large"
        self.url_large = size.source
      elsif size.label == "Original"
        self.url_original = size.source
      end
    end
  end
end

slide_spec.rb:

require 'spec_helper'

describe Slide do
  before(:each) do
    first_photo_id = "444555666"
    @slide = Slide.new(first_photo_id)
  end

  describe "urls" do
    it "should generate the thumbnail url" do
      @slide.url_thumbnail.should match(/_t.jpg$/)
    end

    it "should generate the small url" do
      @slide.url_small.should match(/_m.jpg$/)
    end

    it "should generate the medium500 url" do
      @slide.url_medium500.should match(/.jpg$/)
    end

    it "should generate the medium640 url" do
      @slide.url_medium640.should match(/_z.jpg$/)
    end

    it "should generate the large url" do
      @slide.url_large.should match(/_b.jpg$/)
    end

    it "should generate the original url" do
      @slide.url_original.should match(/_o.jpg$/)
    end
  end

  describe "finding" do
    it "should find the correct last photo" do
      # ???
    end

    it "should find the correct first photo" do
      # ???
    end
  end

  describe "context" do
    it "should return the correct previous photo" do
      # ???
    end

    it "should return the correct next photo" do
      # ???
    end
  end
end


As I understand it, you should be able to do slide.stub!(:flickr).and_return to mock out anything that isn't inside the constructor. I query the fact that the constructor is loading so much from the flickr api though.

Can you change the implementation of slide so that instead of a load of attr_accessors, you have actual methods that get stuff from the flickr api? You should be able to do this without changing the external api of the class, and you can enable speed via caching the results of api calls in instance variables.

If you still want to have all that work done in the constructor, I'd recommend having a default argument that represents the flickr service. Your constructor then becomes the following:

def initialize(id, flickr=flickr)
  … complicated setup code here…
end

This way, when you're testing, just pass in a mock flickr object like so:

 flickr = mock(:flickr)
 @slide = Slide.new(image_id, flickr)

You can then write the usual rspec assertions against the new flickr object (again, without changing the external api).

0

精彩评论

暂无评论...
验证码 换一张
取 消