Har*_*Wit 5 ruby testing lambda unit-testing rspec
我正在创建一个导入功能,可将CSV文件导入到多个表中.我创建了一个模块CsvParser,用于解析CSV文件并创建记录.我接收创建动作的模型扩展了CsvParser.他们调用CsvParser.create并传递正确的属性顺序和一个可选的lambda value_parser.此lambda将散列中的值转换为优先格式.
class Mutation < ActiveRecord::Base
extend CsvParser
def self.import_csv(csv_file)
attribute_order = %w[reg_nr receipt_date reference_number book_date is_credit sum balance description]
value_parser = lambda do |h|
h["is_credit"] = ((h["is_credit"] == 'B') if h["is_credit"].present?)
h["sum"] = -1 * h["sum"].to_f unless h["is_credit"]
return [h]
end
CsvParser.create(csv_file, self, attribute_order, value_parser)
end
end
Run Code Online (Sandbox Code Playgroud)
我在方法中使用lambda而不是检查的CsvParser.create原因是因为lambda就像属于这个模型的业务规则.
我的问题是我应该如何测试这个lambda.我应该在模型或CsvParser中测试吗?我应该测试lambda本身还是self.import方法数组的结果?也许我应该制作另一个代码结构?
我的CsvParser看起来如下:
require "csv"
module CsvParser
def self.create(csv_file, klass, attribute_order, value_parser = nil)
parsed_csv = CSV.parse(csv_file, col_sep: "|")
records = []
ActiveRecord::Base.transaction do
parsed_csv.each do |row|
record = Hash.new {|h, k| h[k] = []}
row.each_with_index do |value, index|
record[attribute_order[index]] = value
end
if value_parser.blank?
records << klass.create(record)
else
value_parser.call(record).each do |parsed_record|
records << klass.create(parsed_record)
end
end
end
end
return records
end
end
Run Code Online (Sandbox Code Playgroud)
我正在测试模块本身:需要'spec_helper'
describe CsvParser do
it "should create relations" do
file = File.new(Rails.root.join('spec/fixtures/files/importrelaties.txt'))
Relation.should_receive(:create).at_least(:once)
Relation.import_csv(file).should be_kind_of Array
end
it "should create mutations" do
file = File.new(Rails.root.join('spec/fixtures/files/importmutaties.txt'))
Mutation.should_receive(:create).at_least(:once)
Mutation.import_csv(file).should be_kind_of Array
end
it "should create strategies" do
file = File.new(Rails.root.join('spec/fixtures/files/importplan.txt'))
Strategy.should_receive(:create).at_least(:once)
Strategy.import_csv(file).should be_kind_of Array
end
it "should create reservations" do
file = File.new(Rails.root.join('spec/fixtures/files/importreservering.txt'))
Reservation.should_receive(:create).at_least(:once)
Reservation.import_csv(file).should be_kind_of Array
end
end
Run Code Online (Sandbox Code Playgroud)
一些有趣的问题。一些注意事项:
如果我正确理解代码,则 lambda 的第一行和第二行过于复杂。减少它们以使它们更具可读性并且更容易重构:
h["is_credit"] = (h['is_credit'] == 'B') # I *think* that will do the same
h['sum'] = h['sum'].to_f # Your original code would have left this a string
h['sum'] *= -1 unless h['is_credit']
Run Code Online (Sandbox Code Playgroud)看起来你的 lambda 不依赖于任何外部(除了h),所以我会单独测试它。您甚至可以将其设置为常量:
class Mutation < ActiveRecord::Base
extend CsvParser # <== See point 5 below
PARSE_CREDIT_AND_SUM = lambda do |h|
h["is_credit"] = (h['is_credit'] == 'B')
h['sum'] = h['sum'].to_f
h['sum'] *= -1 unless h['is_credit']
[h]
end
Run Code Online (Sandbox Code Playgroud)在不知道其基本原理的情况下,很难说应该将这段代码放在哪里。我的直觉是,这不是 CSV 解析器的工作(尽管一个好的解析器可以检测浮点数并将其从字符串转换?)保持 CSV 解析器可重用。(注意:重读一下,我认为您自己已经回答了这个问题 - 这是与模型相关的业务逻辑。跟着您的直觉走!)
最后,您正在定义 和 方法CsvParser.create。您不需要扩展 CsvParser 来访问它,但如果您在 CsvParser 中有其他功能,请考虑创建CsvParser.create一个名为 create_from_csv_file 之类的普通模块方法