Home > Net >  Rails Rspec allow multiple method call in one line
Rails Rspec allow multiple method call in one line

Time:09-30

desc 'Remove credential state users who no longer request for confirm otp within 10 minutes'
 task failed_user_cleaner: :environment do
 puts "Daily UserRecord Cleaning CronJob started - #{Time.now}"

 @user = User.with_state("credentials").with_last_otp_at(Time.now - 10.minutes)
 Users::Delete.new(@user).destroy_all

 puts "Daily UserRecord Cleaning CronJob ended - #{Time.now}"
end

Above is crop job rake file code.

then I've tried in many times and found in many times. But I couldn't find the way to write unit test case for above job.

Help me to write test case correctly.

here is my spec code

require 'rails_helper'

describe 'users rake tasks' do
  before do
    Rake.application.rake_require 'tasks/users'
    Rake::Task.define_task(:environment)
  end

  context 'when remove credential state users who no longer request for confirm otp within 10 minutes' do
    let(:user) { create(:user, last_otp_at: Time.now - 11.minutes, state: "credentials") }
    let (:run_users_rake_task) do
      Rake.application.invoke_task 'users:failed_user_cleaner'
    end

    it 'calls right service method' do
      @users = Users::Delete.new([user])
      expect(@users).to receive(:destroy_all)

      run_users_rake_task
    end
  end
end

here is the error log

Failures:

  1) users rake tasks when remove credential state users who no longer request for confirm otp within 10 minutes calls right service method
 Failure/Error: expect(@users).to receive(:destroy_all)
 
   (#<Users::Delete:0x0000556dfcca3a40 @user=[#<User id: 181, uuid: nil, phone: " 66969597538", otp_secret: nil, last_otp_at: "2021-09-30 09:32:24.961548000  0700", created_at: "2021-09-30 09:43:24.973818000  0700", updated_at: "2021-09-30 09:43:24.973818000  0700", email: nil, avatar: "https://dummyimage.com/300x300/f04720/153572.png?t...", refresh_token: "eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MzI5Njk4MDQsImV4c...", first_name_en: "Jenise", first_name_th: "Damion", last_name_en: "McCullough", last_name_th: "Beatty", nationality: "TH", thai_national_id: nil, thai_laser_code: nil, company_id: 200, role: nil, state: "credentials", date_of_birth: "2020-10-30 00:00:00.000000000  0700", deleted_at: nil, password_digest: "$2a$04$jfR9X9ci06602tlAyLOoRewTK1lZ12vJ2cZ9Dc2ov4F...", username: "zreejme238", shopname: nil, access_token: nil, locked_at: nil, login_attempts: 0, locale: "th", scorm_completed: false>]>).destroy_all(*(any args))
       expected: 1 time with any arguments
       received: 0 times with any arguments
 # ./spec/tasks/users_spec.rb:19:in `block (3 levels) in <top (required)>'

CodePudding user response:

You are creating two instances of Users::Delete when running this test, one within the test and one within the task. Since the instance within the test is not used, it is incorrect to expect it to receive a message.

Rspec has an expectation, expect_any_instance_of, that will fix this however consider reading the full page since it can create fragile or flaky tests. If you wanted to use this method, your test would look something like:

it 'calls right service method' do
  expect_any_instance_of(Users::Delete).to receive(:destroy_all)

  run_users_rake_task
end

Personally I'd instead check that the expected users were deleted with something like:

it 'removes the user' do
  expect { run_users_rake_task }.to change { User.exists?(id: @user.id) }.to(false)
end

CodePudding user response:

Unless you want to use any_instance_of (which is a code smell) you need to stub the Users::Delete method so that it returns a double and put the expectation on the double:

require 'rails_helper'

describe 'users rake tasks' do
  before do
    Rake.application.rake_require 'tasks/users'
    Rake::Task.define_task(:environment)
  end

  context 'when remove credential state users who no longer request for confirm otp within 10 minutes' do
    let(:user) { create(:user, last_otp_at: Time.now - 11.minutes, state: "credentials") }
    let(:run_users_rake_task) do
      Rake.application.invoke_task 'users:failed_user_cleaner'
    end

    let(:double) do
       instance_double('Users::Delete') 
    end

    before do
      allow(Users::Delete).to receive(:new).and_return(double)
    end

    it 'calls right service method' do
      expect(double).to receive(:destroy_all)
      run_users_rake_task
    end
  end
end

However this really just tells us that the API of the service object is clunky and that you should write a class method which both instanciates and performs:

module Users
  class Delete
    # ...
    def self.destroy_all(users)
      new(users).destroy_all
    end
  end
end
desc 'Remove credential state users who no longer request for confirm otp within 10 minutes'
 #...

 Users::Delete.destroy_all(@user)

 # ...
end
require 'rails_helper'

describe 'users rake tasks' do
  # ...
  context 'when remove credential state users who no longer request for confirm otp within 10 minutes' do
    # ...
    it 'calls right service method' do
      expect(Users::Delete).to receive(:destroy_all)
      run_users_rake_task
    end
  end
end
  • Related