测试一个lambda

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)

use*_*769 4

一些有趣的问题。一些注意事项:

  1. 您可能不应该在 lambda 中返回。只做最后一句话[h]。
  2. 如果我正确理解代码,则 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)
  3. 看起来你的 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)
  4. 在不知道其基本原理的情况下,很难说应该将这段代码放在哪里。我的直觉是,这不是 CSV 解析器的工作(尽管一个好的解析器可以检测浮点数并将其从字符串转换?)保持 CSV 解析器可重用。(注意:重读一下,我认为您自己已经回答了这个问题 - 这是与模型相关的业务逻辑。跟着您的直觉走!)

  5. 最后,您正在定义 和 方法CsvParser.create。您不需要扩展 CsvParser 来访问它,但如果您在 CsvParser 中有其他功能,请考虑创建CsvParser.create一个名为 create_from_csv_file 之类的普通模块方法