Home > Software design >  How to fix a task test in RSpec?
How to fix a task test in RSpec?

Time:05-08

I have a table called Jurisdiction and created that task to update the kind column:

namespace :populate_jurisdiction do
  desc "Populate column kind of jurisdiction model"
  task kind: :environment do
    Jurisdiction.all.each { |jurisdiction|
      case (jurisdiction.kind.nil? || jurisdiction.kind.blank?)
        when jurisdiction.name.parameterize == "federal"
        jurisdiction.kind = 1
        when jurisdiction.name.parameterize == "estadual"
        jurisdiction.kind = 2
        when jurisdiction.name.parameterize == "municipal"
          jurisdiction.kind = 3
        when jurisdiction.name.parameterize == "privado"
        jurisdiction.kind = 4
      end
      jurisdiction.save!
    }
  end
end

Then I created that test

require "spec_helper"

Rails.application.load_tasks

describe "populate_jurisdiction:kind" do
  context "when update kind column of jurisdiction" do
    let(:federal) { create(:jurisdiction, name: "Federal", kind: nil) }
    let(:state) { create(:jurisdiction, name: "Estadual", kind: nil) }
    let(:municipal) { create(:jurisdiction, name: "Municipal", kind: '') }
    let(:particular) { create(:jurisdiction, name: "Privado", kind: '') }

    it "when kind column is updated" do
      Rake::Task["populate_jurisdiction:kind"].invoke
      expect(federal.kind).to eq(1)
      expect(state.kind).to eq(2)
      expect(municipal.kind).to eq(3)
      expect(particular.kind).to eq(4)
    end
  end
end

When I run the task in rails console it works, but when I run the test, I got this error

Failures:

  1) populate_jurisdiction:kind when update kind column of jurisdiction when kind column is updated
     Failure/Error: expect(federal.kind).to eq(1)
     
       expected: 1
            got: nil
     
       (compared using ==)

What I'm doing wrong? How can I fix this test?

CodePudding user response:

When you invoke the rake task, there are no jurisdictions, that's why you're getting nil. For example, federal jurisdiction is only created after the rake task when you call federal.kind.

require "spec_helper"

Rails.application.load_tasks

describe "populate_jurisdiction:kind" do
  context "when update kind column of jurisdiction" do
    let(:federal) { create(:jurisdiction, name: "Federal", kind: nil) }
    let(:state) { create(:jurisdiction, name: "Estadual", kind: nil) }
    let(:municipal) { create(:jurisdiction, name: "Municipal", kind: '') }
    let(:particular) { create(:jurisdiction, name: "Privado", kind: '') }

    it "when kind column is updated" do
      # NOTE: jurisdictions are not created until `let` blocks are called.
      federal
      state
      municipal
      particular      

      Rake::Task["populate_jurisdiction:kind"].invoke

      # NOTE: `let` return values are memoized, second call will just
      #        retrieve the value. You have to reload the models as well
      #        to get the updated values from the database.
      expect(federal.reload.kind).to eq(1)
      expect(state.reload.kind).to eq(2)
      expect(municipal.reload.kind).to eq(3)
      expect(particular.reload.kind).to eq(4)
    end
  end
end

https://relishapp.com/rspec/rspec-core/v/3-11/docs/helper-methods/let-and-let

CodePudding user response:

You're invoking the rake task before any of the Jurisdiction's are created. When you say this:

let(:federal) { create(:jurisdiction, name: "Federal", kind: nil) }

that will create federal when you first access it in a test and then remember that for the duration of the test. So there is no federal until after your rake task runs. If you used let! instead of let and reloaded the jurisdictions after the task runs, you'd get better results.


BTW, your rake task doesn't work they way you think it does. There are two forms of case:

case expr
when value1
  ...
when value2
  ...
end

and

case
when expr1
  ...
when expr2
  ...
end

You're using the first form when you mean to be using the second and your code is working by accident. I suspect that all your kinds are nil when you run this, otherwise you'd end up doing:

case false
...
end

and you'd go into the first branch where the jurisdiction.name.parameterize test failed.

Your task should look more like:

Jurisdiction.all.reject { |j| j.kind.blank? }.each do |jurisdiction|
  case jurisdiction.name.parameterize
  when 'federal'
    jurisdiction.kind = 1
  when 'estadual'
    jurisdiction.kind = 2
  when 'municipal'
    jurisdiction.kind = 3
  when 'privado'
    jurisdiction.kind = 4
  end
  jurisdiction.save!
end

or:

Jurisdiction.all.reject { |j| j.kind.blank? }.each do |jurisdiction|
  jurisdiction.kind = case jurisdiction.name.parameterize
                      when 'federal' then 1
                      when 'estadual' then 2
                      when 'municipal' then 3
                      when 'privado' then 4
                      end
  jurisdiction.save!
end

If kind is an integer then kind.blank? will only true when kind.nil? so that can be pushed into the database:

Jurisdiction.where(kind: nil).each do |jurisdiction|
  jurisdiction.kind = case jurisdiction.name.parameterize
                      when 'federal' then 1
                      when 'estadual' then 2
                      when 'municipal' then 3
                      when 'privado' then 4
                      end
  jurisdiction.save!
end

and it looks like #parameterize in this case is only going to be converting the name to lower case so push all the logic into the database:

# This is an SQL CASE expression, not a Ruby one
Jurisdiction.where(kind: nil).update_all(%q(
  kind = case lower(name)
         when 'federal' then 1
         when 'estadual' then 2
         when 'municipal' then 3
         when 'privado' then 4
         end
))
  • Related