Code Monkey home page Code Monkey logo

single_cov's Introduction

Single Cov CI

Actionable code coverage.

rspec spec/foobar_spec.rb
......
114 example, 0 failures

lib/foobar.rb new uncovered lines introduced (2 current vs 0 configured)
Uncovered lines:
lib/foobar.rb:22
lib/foobar.rb:23:6-19
  • Missing coverage on every ๐Ÿ’š test run
  • Catch coverage issues before making PRs
  • Easily add coverage enforcement for legacy apps
  • 2-5% runtime overhead on small files, compared to 20% for SimpleCov
  • Branch coverage (disable via branches: false)
  • Use with forking_test_runner for exact per test coverage
# Gemfile
gem 'single_cov', group: :test

# spec/spec_helper.rb ... load single_cov before rails, libraries, minitest, or rspec
require 'single_cov'
SingleCov.setup :rspec # or :minitest

# spec/foobar_spec.rb ... add covered! call to test files
require 'spec_helper'
SingleCov.covered!

describe "xyz" do ...

Missing target file

Each covered! call expects to find a matching file, if it does not:

# change all guessed paths
SingleCov.rewrite { |f| f.sub('lib/unit/', 'app/models/') }

# mark directory as being in app and not lib
SingleCov::RAILS_APP_FOLDERS << 'presenters'

# add 1-off
SingleCov.covered! file: 'scripts/weird_thing.rb'

Known uncovered

Add the inline comment # uncovered to ignore uncovered code.

Prevent addition of new uncovered code, without having to cover all existing code by marking how many lines are uncovered:

SingleCov.covered! uncovered: 4

Making a folder not get prefixed with lib/

For example packwerk components are hosted in public and not lib/public

SingleCov::PREFIXES_TO_IGNORE << "public"

Missing coverage for implicit else in if or case statements

When a report shows for example 1:14-16 # else, that indicates that the implicit else is not covered.

# needs 2 tests: one for `true` and one for `false`
raise if a == b

# needs 2 tests: one for `when b` and one for `else`
case a
when b
end

Verify all code has tests & coverage

# spec/coverage_spec.rb
SingleCov.not_covered! # not testing any code in lib/

describe "Coverage" do
  # recommended
  it "does not allow new tests without coverage check" do
    # option :tests to pass custom Dir.glob results
    SingleCov.assert_used
  end

  # recommended
  it "does not allow new untested files" do
    # option :tests and :files to pass custom Dir.glob results
    # :untested to get it passing with known untested files
    SingleCov.assert_tested
  end
  
  # optional for full coverage enforcement
  it "does not reduce full coverage" do
    # make sure that nobody adds `uncovered: 123` to any test that did not have it before
    # option :tests to pass custom Dir.glob results
    # option :currently_complete for expected list of full covered tests
    # option :location for if you store that list in a separate file
    SingleCov.assert_full_coverage currently_complete: ["test/a_test.rb"]
  end
end

Automatic bootstrap

Run this from irb to get SingleCov added to all test files.

tests = Dir['spec/**/*_spec.rb']
command = "bundle exec rspec %{file}"

tests.each do |f|
  content = File.read(f)
  next if content.include?('SingleCov.')

  # add initial SingleCov call
  content = content.split(/\n/, -1)
  insert = content.index { |l| l !~ /require/ && l !~ /^#/ }
  content[insert...insert] = ["", "SingleCov.covered!"]
  File.write(f, content.join("\n"))

  # run the test to check coverage
  result = `#{command.sub('%{file}', f)} 2>&1`
  if $?.success?
    puts "#{f} is good!"
    next
  end

  if uncovered = result[/\((\d+) current/, 1]
    # configure uncovered
    puts "Uncovered for #{f} is #{uncovered}"
    content[insert+1] = "SingleCov.covered! uncovered: #{uncovered}"
    File.write(f, content.join("\n"))
  else
    # mark bad tests for manual cleanup
    content[insert+1] = "# SingleCov.covered! # TODO: manually fix this"
    File.write(f, content.join("\n"))
    puts "Manually fix: #{f} ... output is:\n#{result}"
  end
end

Cover multiple files from a single test

When a single integration test covers multiple source files.

SingleCov.covered! file: 'app/modes/user.rb'
SingleCov.covered! file: 'app/mailers/user_mailer.rb'
SingleCov.covered! file: 'app/controllers/user_controller.rb'

Generating a coverage report

SingleCov.coverage_report = "coverage/.resultset.json"
SingleCov.coverage_report_lines = true # only report line coverage for coverage systems that do not support branch coverage

Author

Michael Grosser
[email protected]
License: MIT

single_cov's People

Contributors

alisonacuna avatar batizhevsky avatar bryan-ginger avatar grosser avatar kintner avatar olleolleolle avatar petergoldstein avatar zendesk-jmeade avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

single_cov's Issues

Allow for uncovered range?

Using === instead of == could allow for a range of declared uncovered lines. This could encompass the variability of running under different versions of rails, for example or having versioned code:

if RAILS51 then
  one_line
  two_lines
else
  one_line
end

System/Integration tests?

First off, thanks for this gem (and everything else super high quality that you produce - I knew when I saw your name on this, my dreams of a lightweight Simplecov alternative had been answered).

For these types of tests that don't have an obvious file to tie coverage to, what do you propose to do? I think the integration/system subdirectories should just be ignored, but maybe I'm overlooking something. Ex from my output:

spec/system/dashboard_spec.rb: needs to use SingleCov.covered!

Spring compatibility?

Seeing a lot of app/models/user.rb was expected to be covered, but never loaded. and similar when running tests with spring. Running them with bundle exec doesn't exhibit this behavior.

Allow configuring app folders

Hi @grosser ,

The folders that belong in app/ are hard-coded

file_part[0...0] = if file_part =~ /^(?:#{RAILS_APP_FOLDERS.map { |f| Regexp.escape(f) }.join('|')})\//

I use the gem with Rails and I'd like to cover code in app that is not located in these specific folders :)
(or maybe someone could have a folder in lib that have one of these name ? although it seems less likely)
It would be great to at least allow the user to configure this list, or to make a more flexible guess!

What do you think?
Thanks,
Sulidi

Add option to print the most uncovered files

Hello,

I was thinking that it would be nice to have an option that would result in SingleCov printing the N most uncovered files at the end of the test, along with the number of lines uncovered.

When not on tty "uncovered" is missing a linebreak

# Running:

..............................lib/secrets.rb new uncovered lines introduced (3 current vs 0 configured)
Lines missing coverage:
lib/secrets.rb:18:5-36
lib/secrets.rb:20:5-63
lib/secrets.rb:21:5-28

Branch coverage not showing

Hi Michael,

I'm trying out single_cov and have it working for line coverage, but cannot get the branch coverage to show, which is what attracted me to the project. Would you please help me to understand how I can configure that? It looks like per b70d14e it should just work, but I don't see it in the rspec output. For the purpose of debugging I've thrown together this sample Rails project with rspec with commits separating out the important parts: https://github.com/craig-landry/coverage-project/commits/master

The Book model being tested intentionally has lines that are missing coverage and also missed branches.

When running rspec on that project, here's what I see for output:

โžœ  coverage-project git:(master) โœ— ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]
โžœ  coverage-project git:(master) โœ— rspec
.

Finished in 0.0069 seconds (files took 1.12 seconds to load)
1 example, 0 failures

app/models/book.rb new uncovered lines introduced (2 current vs 0 configured)
Lines missing coverage:
app/models/book.rb:7
app/models/book.rb:9

Thanks,
Craig

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.