我是捣蛋/嘲笑的新手。
如何从外部库中存根方法,这样我只能在不实际调用库的情况下测试模块的方法?
另外,我想知道,我的方法是编写这个模块的方法还是违反了一些重要的编程原则?
# 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
答案 0 :(得分:2)
最简单的方法是使用Dependency Injection的原则。您可以将任何外部依赖项传递给您正在测试的类。在这种情况下是@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
当你对正在发生的事情感到更加自在时。