Estoy creando una característica de importación que importa archivos CSV en varias tablas. Hice un módulo llamado CsvParser
que analiza un archivo CSV y crea registros. Mis modelos que reciben las acciones de crear extienden el CsvParser
. Realizan una llamada al CsvParser.create
y pasan el orden de atributo correcto y un lambda opcional llamado value_parser
. Esta lambda transforma los valores en un hash a un formato preffered.Prueba de una 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
La razón por la que estoy usando una lambda en lugar de cheques dentro del método CsvParser.create
es debido a que el lambda es como una regla de negocio que pertenece a este modelo.
Mi pregunta es cómo debería probar esta lambda. ¿Debo probarlo en el modelo o en CsvParser? ¿Debo probar la lambda o el resultado de una matriz del método self.import
? Tal vez debería hacer otra estructura de código?
Mi CsvParser se ve de la siguiente manera:
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
Estoy probando el propio módulo: requieren '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
Gracias por su respuesta exhaustiva, clara. Seguí tu consejo y pude probar la constante porque podría llamar a Class ::. Solo me pregunto cómo debería probar algo como esto cuando sería una variable en un método. –
Si la lambda no se pudiera refactorizar, lo probaría como cualquier otro método. Si se trata de una lambda muy complicada, dependiendo de muchas variables locales, esto puede indicar que quiere ser su propia instancia. Como regla general, los métodos que son difíciles de probar necesitan un refactor. – user208769
Hoy me di cuenta de que hay algo llamado objeto de método. Se puede usar de la misma manera que una lambda con la única diferencia de que puedo usar el código de un método en lugar de una lambda.Creo que es más limpio que definir un lambda como una constante. Entonces, en lugar de pasar el lambda como parámetro, ahora uso el método (:) –