2012-04-11 3 views
5

Я создаю функцию импорта, которая импортирует CSV-файлы в несколько таблиц. Я создал модуль под названием CsvParser, который анализирует CSV-файл и создает записи. Мои модели, которые получают действия create, расширяют CsvParser. Они делают звонок до CsvParser.create и передают правильный порядок атрибутов и необязательную лямбда под названием 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 

Причина, по которой я использую лямбда вместо чеков внутри метода CsvParser.create потому, что лямбда, как бизнес-правило, принадлежит к этой модели.

Мой вопрос: как я должен проверить эту лямбду. Должен ли я тестировать его в модели или CsvParser? Должен ли я проверить сам лямбда или результат массива метода 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 

Я проверяю сам модуль: требуется '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 

ответ

4

Некоторые интересные вопросы. Несколько примечаний:

  1. Возможно, вы не должны иметь возврат в лямбда. Просто сделайте последнее утверждение [h].
  2. Если я правильно понимаю код, первая и вторая строки вашей лямбда сложны. Уменьшите их, чтобы сделать их более читаемыми и легче реорганизовать:

    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'] 
    
  3. Похоже, ваш лямбда не зависит ни от чего внешнего (кроме 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 
    
  4. Не зная обоснования, что трудно сказать, где вы должны поместить этот код. Мой интуитивный инстинкт заключается в том, что это не работа с парсером CSV (хотя хороший парсер может обнаруживать числа с плавающей запятой и преобразовывать их из строк?) Храните ваш анализатор CSV повторно. (Примечание: перечитывая, я думаю, что вы сами ответили на этот вопрос - это бизнес-логика, привязанная к модели. Пойдите с вашей кишкой!)

  5. Наконец, вы определяете и метод CsvParser.create. Вам не нужно продлить CsvParser, чтобы получить доступ к нему, хотя, если у вас есть другие объекты в CsvParser, рассмотреть возможность CsvParser.create нормальный метод модуля под названием что-то вроде create_from_csv_file

+0

Спасибо за обстоятельный, четкий ответ. Я последовал вашим советам и смог проверить константу, потому что я мог назвать Class :: <имя константы>. Мне просто интересно, как я должен тестировать что-то подобное, когда это будет переменная в методе. –

+1

Если лямбда не может быть реорганизована, я бы проверил ее как любой другой метод. Если это очень сложная лямбда, опираясь на множество локальных переменных, это может указывать на то, что она хочет быть ее собственным экземпляром. Как правило, методы, которые трудно тестировать, нуждаются в рефакторе! – user208769

+0

Сегодня я понял, что есть что-то, называемое объектом метода. Его можно использовать так же, как лямбда, с той лишь разницей, что я могу использовать код метода вместо лямбда.Я думаю, что это более чище, чем определение лямбда как константы. Поэтому вместо передачи лямбда в качестве параметра я теперь использую метод (: ) –

Смежные вопросы