Skip to content

Create a STANDARD_RESPONSES file for common issues #340

Open
@bf4

Description

@bf4

From a comment I wrote in #235

This PR is for you:

If anyone wants to help put together a 'standard response' doc, that would be great. See e.g. the one nokogiri uses https://github.com/sparklemotion/nokogiri/blob/master/STANDARD_RESPONSES.md

Where coverage comes from

So, first, background on how simplecov gets coverage.

SimpleCov uses the stdlib Coverage module.

The Coverage module, when active, tracks when code is evaluated for the first time (i.e. when loaded or run )

SimpleCov starts and stops tracking coverage (via Coverage) when you run SimpleCov.start and SimpleCov.result, respectively.

Thus, when you run SimpleCov.start, load/require a file/files, then run SimpleCov.result (which by default runs via an exit hook when your code finishes running), you will see a certain amount of 'code coverage'. That is, simply loading or requiring a Ruby file will result in some 'code coverage'. If you were to load all files in your app, then run SimpleCov.start, then run your tests, you would only capture the additional coverage for code that is executed for the first time. Likewise, if you ran SimpleCov.start, then loaded all files in your app, then ran the tests, even non-tested files would show 'coverage' as they will have code executed simple by being loaded/required.

Where SimpleCov.start should happen

As described above, as early as possible; it must be before any app code is run.

When you require 'simplecov', the .simplecov file, if present, will be executed.

Thus, if you, perhaps through Bundle.require require simplecov, but do not have a .simplecov file, coverage will not start until you explicitly run SimpleCov.start. In other words, by using a .simplecov file, you ensure that SimpleCov will start as soon as it is required.

The .simplecov

https://github.com/colszowka/simplecov/blob/master/lib/simplecov/defaults.rb#L85

# Autoload config from ~/.simplecov if present
# Autoload config from .simplecov if present

Common Problems

  • Running Rails tests using rake often gives erroneous coverage because the app is loaded before SimpleCov.start is run in a test helper.
  • Coverage lost when running in parallel

Rails and Railties

Vanilla Rails will evaluate all Railties before initializing the Rails app and its initializers. Thus, a Railtie is a reasonable way to hook into Rails initialization lifecycle to start SimpleCov before app code is run.

Right now, require 'simplecov/railtie' loads the file

module SimpleCov
  class Railtie < ::Rails::Railtie
    rake_tasks do
      load 'simplecov/railties/tasks.rake'
    end
  end
end

What should now be obvious is that this railtie does not require 'simplecov' nor does it call SimpleCov.start. @envygeeks is correct that missing the require is really a bug. (but checking the Rails env isn't necessary, because presumably you are only requiring simplecov in test or whenever you want to run it.)

require 'simplecov'
module SimpleCov
  class Railtie < ::Rails::Railtie
    rake_tasks do
      load 'simplecov/railties/tasks.rake'
    end
  end
end

and to get around where you put the SimpleCov.start in your code is why I consider using a .simplecov file a best practice.

Parallel testing

Using .simplecov rather than separately requiring SimpleCov multiple times is recommended if you are merging multiple test frameworks like Cucumber and RSpec that rely on each other, as invoking SimpleCov multiple times can cause coverage information to be lost.

You'll also want to use multiple 'command_names' to differentiate reports being merged in together.

See https://github.com/colszowka/simplecov/blob/master/lib/simplecov/command_guesser.rb#L19 that sets an unset command_name from the env if ENV['PARALLEL_TEST_GROUPS'] && ENV['TEST_ENV_NUMBER'], or from the command that ran the tests, or from any defined constants. to be sure, you may want to set it yourself, e.g. command_name "rails_app_#{$$}" # $$ is the processid

And then add something like merge_timeout 3600 # 1 hour should cover how long it takes the tests to run

Example .simplecov

# https://github.com/colszowka/simplecov#using-simplecov-for-centralized-config
# Maybe put some conditional here not to execute the code below unless ENV['COVERAGE'] == 'true'
SimpleCov.start do
  # see https://github.com/colszowka/simplecov/blob/master/lib/simplecov/defaults.rb
  load_profile 'rails' # load_adapter < 0.8
  coverage_dir 'tmp/coverage'
  # Use multiple 'command_names' to differentiate reports being merged in together
  command_name "rails_app_#{$$}" # $$ is the processid
  merge_timeout 3600 # 1 hour
  add_group "Jobs",        "app/jobs"
  add_group "Middleware",  "app/middleware"
  add_group "Serializers", "app/serializers"
  add_group "Engines",     "engines"
  add_group "Views",       "app/views"
  add_group "Long files" do |src_file|
    src_file.lines.count > 100
  end
  class MaxLinesFilter < SimpleCov::Filter
    def matches?(source_file)
      source_file.lines.count < filter_argument
    end
  end
  add_group "Short files", MaxLinesFilter.new(5)

  # Exclude these paths from analysis
  add_filter 'lib/plugins'
  add_filter 'vendor'
  add_filter 'bundle'
end

or even define your own profile that is backwards compatible

if SimpleCov.respond_to?(:profiles)
  SimpleCov.profiles
else
  SimpleCov.adapters
end.define 'my_app' do
  if defined?(load_profile)
    load_profile  'test_frameworks'
  else
    load_adapter 'test_frameworks'
  end
  coverage_dir 'tmp/coverage'
  # Use multiple 'command_names' to differentiate reports being merged in together
  command_name "rails_app_#{$$}" # $$ is the processid
  merge_timeout 3600 # 1 hour
end
if ENV['COVERAGE'] == 'true'
  SimpleCov.start 'my_app'
   if ENV['CODECLIMATE_REPO_TOKEN']
    begin
      require "codeclimate-test-reporter"
      # We run in parallel, so output results to disk via TO_FILE
      # and send at end, like tddium config
      # https://github.com/codeclimate/ruby-test-reporter/blob/master/lib/code_climate/test_reporter/formatter.rb#L18
      ENV['TO_FILE'] = 'true'
      SimpleCov.formatters = [
          SimpleCov::Formatter::HTMLFormatter,
          CodeClimate::TestReporter::Formatter
      ]
    rescue LoadError
      STDERR.puts "Could not loade CodeClimate SimpleCov Reporter"
    end
  end
end
SimpleCov.at_exit do
  File.open(File.join(SimpleCov.coverage_path, 'coverage_percent.txt'), 'w') do |f|
    f.write SimpleCov.result.covered_percent
  end
  SimpleCov.result.format!
end

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions