[英]How to unit test a “disk full” scenario with Ruby RSpec?
我需要單元測試場景,如下所示:
磁盤有1MB的可用空間。 我嘗試將2MB的文件復制到磁盤上。
使用Ruby RSpec執行此操作的最佳方法是什么?
有關詳細信息,我需要對以下文件緩存方法進行單元測試,因為它似乎有一些問題:
def set first_key, second_key='', files=[]
# If cache exists already, overwrite it.
content_dir = get first_key, second_key
second_key_file = nil
begin
if (content_dir.nil?)
# Check the size of cache, and evict entries if too large
check_cache_size if (rand(100) < check_size_percent)
# Make sure cache dir doesn't exist already
first_cache_dir = File.join(dir, first_key)
if (File.exist?first_cache_dir)
raise "BuildCache directory #{first_cache_dir} should be a directory" unless File.directory?(first_cache_dir)
else
FileUtils.mkpath(first_cache_dir)
end
num_second_dirs = Dir[first_cache_dir + '/*'].length
cache_dir = File.join(first_cache_dir, num_second_dirs.to_s)
# If cache directory already exists, then a directory must have been evicted here, so we pick another name
while File.directory?cache_dir
cache_dir = File.join(first_cache_dir, rand(num_second_dirs).to_s)
end
content_dir = File.join(cache_dir, '/content')
FileUtils.mkpath(content_dir)
# Create 'last_used' file
last_used_filename = File.join(cache_dir, 'last_used')
FileUtils.touch last_used_filename
FileUtils.chmod(permissions, last_used_filename)
# Copy second key
second_key_file = File.open(cache_dir + '/second_key', 'w+')
second_key_file.flock(File::LOCK_EX)
second_key_file.write(second_key)
else
log "overwriting cache #{content_dir}"
FileUtils.touch content_dir + '/../last_used'
second_key_file = File.open(content_dir + '/../second_key', 'r')
second_key_file.flock(File::LOCK_EX)
# Clear any existing files out of cache directory
FileUtils.rm_rf(content_dir + '/.')
end
# Copy files into content_dir
files.each do |filename|
FileUtils.cp(filename, content_dir)
end
FileUtils.chmod(permissions, Dir[content_dir + '/*'])
# Release the lock
second_key_file.close
return content_dir
rescue => e
# Something went wrong, like a full disk or some other error.
# Delete any work so we don't leave cache in corrupted state
unless content_dir.nil?
# Delete parent of content directory
FileUtils.rm_rf(File.expand_path('..', content_dir))
end
log "ERROR: Could not set cache entry. #{e.to_s}"
return 'ERROR: !NOT CACHED!'
end
end
一種解決方案是存根寫入磁盤以引發錯誤的方法。 例如,對於測試磁盤空間錯誤的規范,您可以嘗試:
before do
allow_any_instance_of(File).to receive(:open) { raise Errno::ENOSPC }
# or maybe # allow(File).to receive(:write) { raise Errno::ENOSPC }
# or # allow(FileUtils).to receive(:cp) { raise Errno::ENOSPC }
# or some combination of these 3...
end
it 'handles an out of disk space error' do
expect{ my_disk_cache.set('key1', 'key2', [...]) }.to # your logic for how BuildCache::DiskCache should handle the error here.
end
但是有兩個問題:
1) Errno::ENOSPC
可能不是您實際看到的錯誤。 該錯誤符合您的問題中的描述,但根據您的lib及其運行的系統的特性,您可能不會收到Errno::ENOSPC
錯誤。 也許你首先用盡RAM並且正在使用Errno::ENOMEM
,或者你可能有太多文件描述符打開並且正在獲得Errno::EMFILE
。 當然,如果你想要嚴格,你可以處理所有這些,但這是耗時的,你會得到減少回報處理更隱晦的錯誤。
2)該解決方案涉及在特定類上存根特定方法。 ( File.open
)這並不理想,因為它將測試的設置與代碼中的實現相結合。 也就是說,如果你重構BuildCache::DiskCache#set
為不使用File.open
,那么即使方法可能正確,此測試也可能會失敗。
也就是說, File.open
相當低級。 我知道有些FileUtils
方法使用File.open
(值得注意的是, FileUtils.cp
),所以我只想使用首先建議allow_any_instance_of
線。 我希望能處理你的大部分用例。
或者,有一個名為fakefs
的工具可以幫助您解決這個問題。 我不熟悉它,但它可能具有幫助測試此類錯誤的功能。 你可能想看看它。
您可以使用您需要測試的方法中發生的任何方法調用,並將它們存根以便引發錯誤。 例如, FileUtils.touch
被多次調用,所以我們可以這樣做:
it 'handles file write error gracefully' do
allow(FileUtils).to receive(:touch).and_raise('oh no')
# your expectations
# your test trigger
end
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.