Search code examples
ruby-on-railsrubyrakerake-taskrakefile

writing unit test for a rake task


I have a simple rails application where I import data from csv into my rails app which is functioning properly, but I have no idea where to start with testing this rake task, as well as where in a modular rails app. Any help would be appreciated. Thanks!

csv_importer.task

require 'csv_importer/engine'

task users: :environment do
  desc 'Import users from csv'

  WebImport.new(url: 'http://blablabla/people.csv').call
end

csv_importer.rb

require 'csv_importer/engine'

class WebImport
  def initialize(url)
    @url = url
  end

  def call
    url = 'http://blablabla/people.csv'
    # I forced encoding so avoid UndefinedConversionError "\xC3" from ASCII-8BIT to UTF-8
    csv_string = open(url).read.force_encoding('UTF-8')
    counter = 0
    duplicate_counter = 0

    user = []
    CSV.parse(csv_string, headers: true, header_converters: :symbol) do |row|
      next unless row[:name].present? && row[:email_address].present?
      user = CsvImporter::User.create row.to_h
      if user.persisted?
        counter += 1
      else
        duplicate_counter += 1
      end
    end
    p "Email duplicate record: #{user.email_address} - #{user.errors.full_messages.join(',')}" if user.errors.any?

    p "Imported #{counter} users, #{duplicate_counter} duplicate rows ain't added in total"
  end
end

What I have done:

csv_importer_test.rb

require 'test_helper'
require 'rake'

class CsvImporter::Test < ActiveSupport::TestCase
  test 'truth' do
    assert_kind_of Module, CsvImporter
  end

  test 'override_application' do
    @rake = Rake::Application.new
    Rake.application = @rake
    assert_equal @rake, Rake.application
  end

  test '' do
    # something here
  end
end

This works fine and fills my database. How do I write TestCase to capture this solution?


Solution

  • You've actually done the good thing here about keeping all of your task logic in a library file outside rake. I'd go a little further here...

    require 'csv_importer/engine'
    
    class WebImport
      def initialize(url)
        @url = url
      end
    
      def call
        url = 'http://blablabla/people.csv'
        csv_string = open(url).read.force_encoding('UTF-8')
    
        string_to_users(csv_string)
     end
    
     def string_to_users(csv_string)
        counter = 0
        duplicate_counter = 0
        ....
     end
    end
    

    See here that we're removed how we call our method (we don't care if it's Rake or Ruby calling our method) AND potentially separated how we get our data.

    Next, I would write my test like the following:

    test 'override_application' do
      a = WebImport.new(url: 'http://blablabla/people.csv')
    
      a.string_to_users("a,b,c,d,e")  # <-- a string you saved from some manual run, or that contains maybe a sample record with their exact format
      assert_equal Users.count, 42
    end
    

    Given that you've now seperated:

    • how you call your code, because it's away from Rake in a separate library/module
    • how your code gets it's data (normally being provided data by call... but you can insert data itself here)

    Then you should be all set to go with your test driven design!