Rails / Rspec:不完全理解如何使用double方法或如何模拟对象以用于我的期望

时间:2013-12-12 16:51:51

标签: ruby-on-rails rspec tdd rspec-rails

这是我的模块:

module ManagesVideoFiles

  def self.included(klass)
    klass.before_destroy :cleanup
    klass.validates_presence_of :panda_id
  end

  def panda_video(being_destroyed = false)
    @panda_video ||= panda_id ? Panda::Video.find(panda_id) : nil
  rescue Panda::APIError => e
    if e.message =~ /RecordNotFound/i
      self.destroy unless being_destroyed
      nil
    end
  end

  def thumbnail_url(style = :web)
    return read_attribute(:thumbnail_url) if read_attribute(:thumbnail_url).present?

    return nil unless panda_video
    return nil unless encoding = get_encoding
    return nil if encoding.screenshots.blank?

    secure_screenshot = encoding.screenshots.first.sub "http", "https"

    self.update_attribute(:thumbnail_url, secure_screenshot)
    return read_attribute(:thumbnail_url)
  end

  def mp4_url
    return mp4_video_url unless mp4_video_url.blank?

    return nil unless panda_video
    return nil unless encoding = get_encoding

    secure_url = encoding.try { |enc| enc.url.sub("http", "https") }

    self.update_attribute(:mp4_video_url, secure_url)
  end

  def get_encoding
    return nil unless panda_video

    panda_video.encodings.detect { |vid| vid.encoding_progress == 100 && vid.status == "success" }
  end

private

  def cleanup
    if respond_to?(:from_mobile_device?) && from_mobile_device?
      bucket = AWS::S3.new.buckets[API["s3"][Rails.env]["bucket"] + "-videos"]
      bucket.objects.delete(self.thumbnail_url.split("/").last, self.mp4_video_url.split("/").last)
    elsif panda_video(true)
      panda_video.delete
    end

    true
  end

end

我正在测试的方法是#thumbnail_url ......这是我的测试:

require 'spec_helper'

describe ManagesVideoFiles do

  let(:athlete) { create(:athlete) }
  let(:video) { create(:video, athlete_id: athlete.id) }

  describe "instance methods" do

    describe "#thumbnail_url" do

      context "when video has thumbail_url object property" do
        it "should return thumbnail_url from database" do
          url = "https://foo.com/image.ext"
          video.thumbnail_url = url
          video.save
          video.thumbnail_url.should eq url
        end
      end

      context "when panda_id is nil" do
        it "should return nil" do
          video.stub(:panda_id).with(nil)
          video.stub(:panda_video).and_return nil
          video.thumbnail_url.should be_nil
        end
      end

      context "when panda can find video", focus: true do
        it "should return encoding object with screenshots" do
          video.stub(:panda_video).and_return double(encodings: [double(panda_encoding_object(100, :success))])
          video.get_encoding.stub(:screenshots).and_return [ double({ url: "http://foo.com/image.ext" }) ]
          video.thumbnail_url
          video.thumbnail_url.should eq video.get_encoding.screenshots.first.url.sub "http", "https"
        end
      end

    end

    describe "#mp4_url" do

      context "when encoding is available from panda" do
        it "should set mp4 url to encoding from panda object" do
          video.stub(:panda_video).and_return double(encodings: [double(panda_encoding_object(100, :success))])
          video.mp4_url
          video.mp4_video_url.should eq video.panda_video.encodings.first.url.sub("http", "https")
        end
      end

      context "when encoding is not available from panda" do
        it "should return nil" do
          video.stub(:panda_id).with(nil)
          video.stub(:panda_video).and_return nil
          video.mp4_url.should be_nil
        end
      end

      context "when encoding is available from panda but progress is less than 100" do
        it "should return nil" do
          video.stub(:panda_video).and_return double(encodings: [double(panda_encoding_object(50, :success))])
          video.mp4_video_url.should be_nil
        end
      end

      context "when encoding is available from panda but status has failed" do
        it "should return nil" do
          video.stub(:panda_video).and_return double(encodings: [double(panda_encoding_object(100, :fail))])
          video.mp4_video_url.should be_nil
        end
      end

    end

    describe "#get_encoding" do

      context "when encoding is unavailable from panda" do
        it "should return nil" do
          video.stub(:panda_video).and_return nil
          video.get_encoding.should be_nil
        end
      end

      context "when encoding is available from panda" do
        it "should return panda object" do
          video.stub(:panda_video).and_return double(encodings: [double(panda_encoding_object(100, :success))])
          video.get_encoding.should eq video.panda_video.encodings.first
        end
      end

    end

  end

end

def panda_encoding_object progress, status
  { url: "http://hello.io", encoding_progress: progress, status: status.to_s }
end

我已经看过使用webmock宝石,但我不确定这是否会完成我在这里尝试做的事情。我在测试中得到的错误是:

  1) ManagesVideoFiles instance methods #thumbnail_url when panda can find video should return encoding object with screenshots
     Failure/Error: video.thumbnail_url
       Double received unexpected message :sub with ("http", "https")
     # ./app/models/concerns/manages_video_files.rb:24:in `thumbnail_url'
     # ./spec/concerns/manages_video_files_spec.rb:33:in `block (5 levels) in <top (required)>'

1 个答案:

答案 0 :(得分:1)

发生错误是因为方法编码的存根返回的对象没有响应#sub方法(您定义变量secure_screenshots的行)。问题是,当他们获得没有被剔除的方法时,双打会引发错误。

要暂时解决此问题,您可以在测试底部的熊猫编码对象中添加sub的定义,但这只是为了解决此测试文件存在的更大问题

当你有一些试图做太多的课程时,嘲弄和顽固是一场艰苦的斗争。如果您要为存根(或双打)的结果构建存根(或双打),那么您的测试会告诉您现在是时候将您的类分解为更小,更简单的对象。