0

我是存根/嘲笑的新手。

如何从外部库中存根方法,以便我只能测试模块的方法而不实际调用库?

另外,我想知道,我编写这个模块的方法是可行的还是违反了一些重要的编程原则?

# file_module.rb
module FileModule
  require 'net/ftp'

  @ftp = nil

  def self.login    
    if !@ftp || @ftp.closed?
      @ftp = Net::FTP.new(Rails.configuration.nielsen_ftp_server)
      @ftp.login(Rails.configuration.nielsen_ftp_user, Rails.configuration.nielsen_ftp_password)
    end
  end

  def self.get_list_of_files_in_directory(directory, type)
    login
    @ftp.chdir("/#{directory}")        
    files = case type
      when "all"            then @ftp.nlst("*")
      when "add"            then @ftp.nlst("*add*")
    end
  end
end

# file_module_spec.rb (RSpec)    
require 'spec_helper'
describe NielsenFileModule do
  describe ".get_list_of_files_in_directory" do
    it "returns correct files for type all" do
      # how to mock Net::FTP or stub all its methods so I simulate the return value of @ftp.nlst("*")?
      NielsenFileModule.get_list_of_files_in_directory("test_folder", "all").count.should eq 6
    end
  end
end  
4

1 回答 1

3

想到这一点的最简单方法是使用依赖注入原理。您可以将任何外部依赖项传递给您正在测试的类。在这种情况下,@ftp 对象。

您在对象上使用成员变量以及类(或静态)方法时犯了一个错误。

考虑修改您的类以执行以下操作:

# file_module.rb
module FileModule
  require 'net/ftp'
  attr_accessor :ftp

  @ftp = Net::FTP.new(Rails.configuration.nielsen_ftp_server)

  def login    
    if !@ftp || @ftp.closed?
      @ftp.login(Rails.configuration.nielsen_ftp_user, Rails.configuration.nielsen_ftp_password)
    end
  end

  def get_list_of_files_in_directory(directory, type)
    login
    @ftp.chdir("/#{directory}")        
    files = case type
      when "all"            then @ftp.nlst("*")
      when "add"            then @ftp.nlst("*add*")
    end
  end
end

现在在您的测试中,您可以测试模块上的对象方法,而不是测试模块上的类方法。

require 'spec_helper'
class FileClass
  include FileModule
end

let(:dummy) { FileClass.new }
let(:net_ftp) { double(Net::FTP) }
before { dummy.ftp = net_ftp }

describe FileModule do
  describe '.login' do
    context 'when ftp is not closed' do
      before { net_ftp.stub(:closed) { true } }
      it 'should log in' do
        net_ftp.should_receive(:login).once
        dummy.login
      end
    end
  end
end

现在您可以存根,或对您的 net_ftp 对象设置期望,如上所示。

注意:有很多方法可以做到这一点,但这是一个很好的例子,很有意义。您正在将外部服务提取为可以加倍并用模拟功能替换的东西。

您还可以存根类方法并执行以下操作:

Net::FTP.any_instance.stub

当你对正在发生的事情感到更舒服时。

于 2013-06-25T14:17:44.403 回答