【问题标题】:How to test ActiveSupport::TaggedLogging如何测试 ActiveSupport::TaggedLogging
【发布时间】:2020-03-19 09:41:39
【问题描述】:

我想测试我在代码中生成的特定日志。虽然测试Rails.logger 似乎相当简单,但我将它包装在ActiveSupport::TaggedLogging 中,我无法使用它进行测试......

我有两种方法:

  def logger
    @logger = ActiveSupport::TaggedLogging.new(Rails.logger)
  end

  def log(message)
    logger.tagged('MangaDex::Importer', "User ##{@current_user_id}") do
      logger.info message
    end
  end

然后我在我的代码中这样调用它:

    if md_parsed_list.blank?
      log 'List is inaccessible'
      return
    end

现在,在我的 Rspec 测试中,我一直在尝试做这样的事情:

  it 'logs and stops execution if there is nothing to import' do
    expect(Spiders::Mangadex).to receive(:parse!)
      .with(:parse, url: import_url)
      .and_return({})

    expect(ActiveSupport::TaggedLogging).to receive(Rails.logger)
    expect(Rails.logger).to receive(:info).with('List is inaccessible')
    expect(CreateMangaEntries).not_to receive(:call)

    described_class.perform_async(import_url, user.id)
    described_class.drain
  end

我知道我在连接 TaggedLogging 和 Rails.logger 时缺少一些步骤,因为调用上面的测试只会引发错误 undefined method 'to_sym' for #<ActiveSupport::Logger:0x00007f8fc545db50>

希望得到一些帮助,在此先感谢!

编辑:

在基于@Grzegorz 改进了所涉及的模拟之后,测试最终看起来像这样:

  let(:tagged_logger_double) { instance_double(ActiveSupport::Logger) }

  it 'logs and stops execution if MDList is inaccessible' do
    expect(Spiders::Mangadex).to receive(:parse!)
      .with(:parse, url: import_url)
      .and_return(nil)
    expect(ActiveSupport::TaggedLogging)
      .to receive(:new)
      .with(Rails.logger)
      .and_return(tagged_logger_double)
    expect(CreateMangaEntries).not_to receive(:call)
    expect(tagged_logger_double).to receive(:info).with('List is inaccessible')

    described_class.perform_async(import_url, user.id)
    described_class.drain
  end

由于ActiveSupport::TaggedLogging 的实例为ActiveSupport::Logger,我不得不稍微更改双精度。

我能够解决原始异常,但仍然无法测试 receive(:info),因为 tagged 似乎丢失了: #<InstanceDouble(ActiveSupport::Logger) (anonymous)> received unexpected message :tagged with ("MangaDex::Importer", "User #2626")

但是当我尝试存根该方法时:allow(tagged_logger_double).to receive(:tagged),我得到这样的方法不存在的错误:the ActiveSupport::Logger class does not implement the instance method: tagged,我仍然坚持测试失败。

【问题讨论】:

    标签: ruby-on-rails logging rspec ruby-on-rails-6


    【解决方案1】:

    Grzegorz 正确回答了您问题的第一部分,所以从他离开的地方继续...

    你说得对,你需要存根 tagged。您收到方法未实现错误的原因是 ActiveSupport::Logger 未实现 tagged。被ActiveSupport::TaggedLogging包裹时会被扩展。

    在此处查看构造函数方法定义:https://api.rubyonrails.org/classes/ActiveSupport/TaggedLogging.html#method-c-new

    我不确定在这种情况下如何使用验证替身。一种选择是不这样做,只使用double

    另一个问题是你没有正确地存根tagged。您需要对它进行存根,以便它屈服于块。

    最后,您需要记住logger 方法的返回值,因为您只希望ActiveSupport::TaggedLogging 接收一次new

    综合所有这些,我编写了以下测试:

    require "rails_helper"
    require "custom_logger"
    
    RSpec.describe "CustomLogger" do
      let(:tagged_logger_double) { double("Tagged logger") }
    
      it "logs" do
        expect(ActiveSupport::TaggedLogging)
          .to receive(:new)
          .with(Rails.logger)
          .and_return(tagged_logger_double)
    
        allow(tagged_logger_double)
          .to receive(:tagged)
          .and_yield
    
        expect(tagged_logger_double).to receive(:info).with("message")
    
        CustomLogger.new.log("message")
      end
    end
    

    这通过以下代码,我认为这与您的示例非常相似:

    class CustomLogger
      def log(message)
        logger.tagged("Tag") do
          logger.info message
        end
      end
    
      def logger
        @_logger ||= ActiveSupport::TaggedLogging.new(Rails.logger)
      end
    end
    

    【讨论】:

    • 这就像一个魅力,谢谢!你是对的,我到处都有一个错误,忘记了记忆记录器!我傻了,谢谢你指出来
    【解决方案2】:

    您已将一个对象传递给.to receive 方法,它需要一个方法名称(字符串或符号)。这是您的错误的根源,下面的内容对我来说更有意义:

    expect(ActiveSupport::TaggedLogging)
      .to receive(:new)
      .with(Rails.logger)
      .and_return(tagged_logger_double)
    

    如果您像这样定义它,您将拥有对规范中记录器的完全访问权限

    let(:tagged_logger_double) { instance_double(ActiveSupport::TaggedLogging) }
    

    并设定您的期望

    expect(tagged_logger_double).to receive(:info).with('List is inaccessible')
    

    【讨论】:

    • 这是一个很好的观点,这是定义期望的正确方法。但这并不是解决此问题的全部方法。我现在在tagged 失败了,因为来自logger.tagger 的记录器现在是nil。我想我需要在那里传递一个双精度,而不是一个记录器,然后只传递一个存根方法,但我需要以某种方式将它注入代码中,我不完全确定如何
    • 抱歉,我假设您在解除即时错误的阻止后能够继续。我更新了我的答案,希望对您有所帮助。
    • 谢谢@Grzegorz,嘲讽不是我的强项。您扩展的答案肯定有帮助,但我再次陷入困境。首先,有趣的是,标记为Logger 的实例变成了ActiveSupport::Logger,所以我不得不更新tagged_logger_double。但是现在我无法到达receive(:info),因为我错过了tagged#<InstanceDouble(ActiveSupport::Logger) (anonymous)> received unexpected message :tagged with ("MangaDex::Importer", "User #2626")
    • 但是当我尝试存根该方法时:allow(tagged_logger_double).to receive(:tagged),我得到这样的方法不存在的错误:the ActiveSupport::Logger class does not implement the instance method: tagged,这一切都非常奇怪......
    • @MaximFedotov 抱歉,但 cmets 不是扩展原始问题的最佳场所。如果您被困在其他地方,请发布另一个问题。另外,如果我的回答有帮助,请考虑投票并接受以帮助其他有类似问题的人。
    【解决方案3】:

    我需要测试已记录的标签和消息。 这段代码对我有用。

    let(:logger) { ActiveSupport::TaggedLogging.new(Rails.logger) }
    
    before do
        allow(logger).to receive(:tagged).and_call_original
        allow(logger).to receive(:info).and_call_original
        allow(ActiveSupport::TaggedLogging).to receive(:new).and_return(logger)
    end
    
    it 'logs with correct tags' do
      expect(logger).to have_received(:tagged).with(tags)
    end
    
    it 'logs with correct message' do
      expect(logger).to have_received(:info).with(message)
    end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-10
      • 2011-11-22
      • 2013-04-27
      相关资源
      最近更新 更多