【问题标题】:How can I get nyc/istanbul JavaScript coverage from rspec integration tests如何从 rspec 集成测试中获得 nyc/istanbul JavaScript 覆盖率
【发布时间】:2018-01-31 00:08:34
【问题描述】:

我有一个带有 JavaScript(主要是 React)前端的 Ruby on Rails apprspec 功能测试套件执行了前端 JavaScript 代码的重要部分,但我对此一无所知。同时,JavaScript 单元测试——通过jest 运行——提供 JavaScript 覆盖映射。

我想要 rspec 基于浏览器的集成测试的累积 JavaScript 覆盖率结果,但我没有找到任何示例。 nyc,标准的 JS 覆盖库,应该能够实现这一点——可能与 babel-plugin-istanbul 结合使用。

【问题讨论】:

    标签: javascript ruby-on-rails rspec integration-testing istanbul


    【解决方案1】:

    虽然它没有直接解释如何实现这一点,但您可以了解其他人如何设法在 Ruby on Rails 中实现代码覆盖。你可以看到here 他们使用jest--coverage 标志来获取覆盖结果。

    我认为将 JavaScript 代码覆盖率与其他功能测试分开可能是明智之举,因为那里的每个教程似乎都倾向于将它们分开。

    这是另一个教程,以防万一另一个不是您正在寻找的内容:https://reactjsnews.com/setting-up-rails-for-react-and-jest

    【讨论】:

      【解决方案2】:

      您可以使用 capybara 系统/功能测试来捕获伊斯坦布尔的 JS 代码覆盖率。

      我们是用 webpack 做的,它可能可以用资产管道来做,但我不知道从哪里开始。

      一般来说,您需要将 istanbul 加入您的 webpack 管道,然后您需要从每个页面中删除 __coverage__ 变量,然后再移动到测试中的下一页。

      yarn add istanbul-instrumenter-loader -D

      yarn add nyc -D

      在 config/webpack/development.js - 是的,开发,而不是测试 - 确保你没有 NODE_ENV === 测试

      if (process.env.CI) {
        environment.loaders.append('istanbul-instrumenter', {
          test: /(\.js)$/,
          use: {
            loader: 'istanbul-instrumenter-loader',
            options: { esModules: true },
          },
          enforce: 'post',
          exclude: /node_modules/,
        })
      }
      

      然后您需要抓取这些功能/系统测试。我们正在使用带有 rspec 的系统测试 - 这是我们运行的帮助程序

      spec/support/js_code_coverage_helper.rb

      # frozen_string_literal: true
      
      # ugly hack to get in between the teardown of the browser session to collect the JS info for coverage - have to grab it
      # from the rails system test because rspec doesn't expose it and the after block doesn't fire in the right place.
      module JsCodeHelpers
        def before_teardown
          dump_js_coverage
          super
        end
      
        def dump_js_coverage
          return if page.driver.class.name == 'Capybara::RackTest::Driver' # HACK
          return unless ENV['CI']
      
          coverage_report_dir = '.nyc_output'
          FileUtils.mkdir_p coverage_report_dir if ENV['CI']
          page_coverage = page.evaluate_script('JSON.stringify(window.__coverage__);')
      
          return if page_coverage.blank?
      
          File.open(Rails.root.join(coverage_report_dir, "system_test_#{Time.current.to_i}.json"), 'w') do |report|
            report.puts page_coverage
          end
        end
      end
      
      require 'action_dispatch/system_testing/test_helpers/setup_and_teardown'
      
      ActionDispatch::SystemTesting::TestHelpers::SetupAndTeardown.prepend(JsCodeHelpers)
      
      # another ugly hack to scrape coverage information before any potential element navs us away from the page we're on - if we move to
      # another page then we lose the __coverage__ variable. we do end up multi-capturing coverage - that's not important now.
      require 'capybara/node/element'
      module ClickTaker
        # private method that all the clicks use - this is as close as we can get to the bare browser easily
        def perform_click_action(keys, wait: nil, **options)
          dump_js_coverage
          super
        end
      
        def select_option(wait: nil)
          dump_js_coverage
          super
        end
      
        def send_keys(*args)
          dump_js_coverage
          super
        end
      
        def trigger(event)
          dump_js_coverage
          super
        end
      
        def drag_to(node, **options)
          dump_js_coverage
          super
        end
      
        def drop(*args)
          dump_js_coverage
          super
        end
      
        def execute_script(script, *args)
          dump_js_coverage
          super
        end
      
        def evaluate_script(script, *args)
          dump_js_coverage
          super
        end
      
        def evaluate_async_script(script, *args)
          dump_js_coverage
          super
        end
      
        def dump_js_coverage
          return if session.driver.class.name == 'Capybara::RackTest::Driver' # HACK
          return unless ENV['CI']
      
          coverage_report_dir = '.nyc_output'
          FileUtils.mkdir_p coverage_report_dir if ENV['CI']
          page_coverage = session.evaluate_script('JSON.stringify(window.__coverage__);')
      
          return if page_coverage.blank?
      
          File.open(Rails.root.join(coverage_report_dir, "system_test_#{Time.current.to_i}.json"), 'w') do |report|
            report.puts page_coverage
          end
        end
      end
      
      Capybara::Node::Element.prepend(ClickTaker)
      

      这是一个 WIP/hack,直到我找到一种更好的方法来捕获页面转换之间的所有内容 - 但它效果很好。

      请注意,我们的配置需要 CI=true 才能运行 - 这对远程来说很简单,但 CI=true rspec spec/system 将为您提供 js 覆盖率

      然后你可以对 .nyc_output 中的文件做任何你需要的事情

      yarn nyc report

      yarn nyc merge ./.nyc_output coverage.json

      唯一的缺点是,如果它在转到下一页之前捕获文件(所有点击...),它会在一行(行数)上显示同一覆盖范围的多个点击,这对我们来说更好我们不会对所有事情都使用远程表单 - 如果我们在提交/发布后在新页面上完成测试,我们会丢失在表单上运行的任何内容。

      【讨论】:

      • 在我的项目中,我们最近找到了一个类似的解决方案,使用 JSCover 而不是 nycgithub.com/WikiEducationFoundation/WikiEduDashboard/pull/4131/…
      • 一个技巧可能适用于您的设置,就像对我们的设置一样,是对 Capybara Selenium 驱动程序使用 clear_local_storage: false。这让我们可以在整个测试运行中建立覆盖范围,而不必担心在页面加载之间会重置任何内容。
      • 感谢圣人的想法!不幸的是,更改 clear_local_storage 不起作用 - 我们仍然需要点击重载。
      猜你喜欢
      • 1970-01-01
      • 2018-10-09
      • 2019-06-28
      • 2019-12-14
      • 2020-06-29
      • 1970-01-01
      • 2016-04-12
      • 2018-05-21
      • 1970-01-01
      相关资源
      最近更新 更多