为什么我的rake任务在测试中运行两次?

8

我有一个草率任务测试,按照我在网上找到的唯一示例设置。

它看起来像这样:

require 'test_helper'
require 'minitest/mock'
require 'rake'

class TestScrapeWelcome < ActiveSupport::TestCase
  def setup
    Rake.application.init
    Rake.application.load_rakefile

    @task = Rake::Task['scrape:scrape']
    @task.reenable
  end

  def teardown
    Rake::Task.clear
  end

  test "scraping text and sending to elasticsearch" do
    mocked_client = Minitest::Mock.new
    get_fixtures.each_with_index do |arg,i|
      mocked_client.expect :index, :return_value, [index: "test", type: 'welcome', id: i, body: arg]
    end
    Elasticsearch::Model.stub :client, mocked_client do
      @task.invoke
    end
    assert mocked_client.verify
  end

  private

  def get_fixtures
    (0..11).map { |i|
      File.read("test/fixtures/scrape/index_#{i}.json")
    }
  end

end

但是,任务运行一次之后,它开始再次运行,而我并没有做任何操作(puts@task.invoke 前后打印显示,任务只运行了一次)。
4个回答

12

原来 rake 已经在测试运行时被要求和初始化了,所以所有以下的代码行都需要被移除,否则任务将被定义两次并且即使你只调用一次它也会运行两次。

require 'minitest/mock'
require 'rake'
...
Rake.application.init
Rake.application.load_rakefile

5

针对Rails 5.1(使用minitest)的最新答案:

我发现我需要以下内容才能加载任务一次且仅一次:

MyAppName::Application.load_tasks if Rake::Task.tasks.empty?

如果你不介意在运行不需要它们的单独测试时加载任务,可以将 MyAppName::Application.load_tasks 添加到你的 test_helper 中。

(将 MyAppName 替换为你的应用程序名称)


1
如果您有任何添加了rake任务的gem,那么这个答案将导致您的应用程序任务无法加载,因为此时Rake::Task.tasks不会为空。 - brainbag
我使用的所有宝石都需要明确地添加到rake中,所以这对我很有效。你发现了哪些宝石会自动将它们的rake任务添加到应用程序中?在那种情况下,你会如何解决这个问题? - iheggie
与贾里德一起微笑-我明白。 我仍然有一个使用Rails 2.3.18 LTS的客户(他们可能永远不会升级,因为他们没有预算来更新,而现在有一个免费的LTS版本)。 不幸的是,较新的LTS版本是收费的。 - iheggie
我删除了之前的评论。@iheggie提到我写过我仍在使用Rails 3.2,而这个解决方案对我来说仍然有效,即使是在旧版的Rails上。不幸的是,除了rake test之外,其他任务都无法正常工作,因此可以说rake db:migrate失败了,因为rake不知道如何构建它。但是我会继续尝试,因为这是一个好的线索,并且我稍后会发布我的发现。 - Jared
好的,对我来说,在Rails 3.2中,任务在那个时候不是空的,所以它从来没有起作用,只有现有的任务被加载,这些任务是:默认和测试。这意味着没有其他任务会被加载。现在我将发布我的解决方案,实际上只是这个版本的一个版本。 - Jared

3

我尝试了@iheggie的回答,但它的作用是确实只运行了一次测试,但任何其他任务都会出现“Don't know how to build task '<task_name_like_db_migrate>'”错误。

我仍然在使用Rails 3.2。原来有几个任务预先加载,因此Rake::Task.tasks.empty?从未为true,所有其他有用的任务也未被加载。我对此进行了一些调整,目前这个版本适合我的情况:

Rake::Task.clear if Rails.env.test?
MyAppName::Application.load_tasks

希望这对任何人有所帮助。

1
Rake::Task.clear 对我很有用! - laurajaime

0
一个解决方案是测试一个已经被制作为Railtie的Gem任务,以便可以将任务添加到Rails应用程序中:
当您在spec_helper.rb中定义Rails::Application类(允许您的测试调用Rails.application.load_tasks)时,请不要在测试模式下定义Railtie。否则,Rake文件将会被加载一次作为Railtie,一次作为Engine:
class Railtie < Rails::Railtie
  rake_tasks do
    load 'tasks/mygem.rake'
  end
end unless Rails.env.test? # Without this condition tasks under test are run twice

另一个解决方案是在Rake文件中加入条件,如果文件已经被加载,则跳过任务定义。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接