{"id":13879486,"url":"https://github.com/grosser/single_cov","last_synced_at":"2025-05-16T10:08:37.452Z","repository":{"id":56445832,"uuid":"56417962","full_name":"grosser/single_cov","owner":"grosser","description":"Actionable code coverage.","archived":false,"fork":false,"pushed_at":"2025-04-10T01:24:10.000Z","size":210,"stargazers_count":231,"open_issues_count":5,"forks_count":11,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-05-11T19:08:21.073Z","etag":null,"topics":["code-cov","code-coverage","code-quality","coverage","ruby"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/grosser.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":"MIT-LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-04-17T03:55:48.000Z","updated_at":"2025-04-10T01:24:13.000Z","dependencies_parsed_at":"2024-01-13T20:57:19.344Z","dependency_job_id":"4449b89c-7966-4ef3-a2fb-69efb56d38dd","html_url":"https://github.com/grosser/single_cov","commit_stats":null,"previous_names":[],"tags_count":49,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grosser%2Fsingle_cov","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grosser%2Fsingle_cov/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grosser%2Fsingle_cov/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grosser%2Fsingle_cov/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/grosser","download_url":"https://codeload.github.com/grosser/single_cov/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254509477,"owners_count":22082892,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["code-cov","code-coverage","code-quality","coverage","ruby"],"created_at":"2024-08-06T08:02:22.566Z","updated_at":"2025-05-16T10:08:32.441Z","avatar_url":"https://github.com/grosser.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# Single Cov [![CI](https://github.com/grosser/single_cov/actions/workflows/actions.yml/badge.svg)](https://github.com/grosser/single_cov/actions?query=branch%3Amaster)\n\nActionable code coverage.\n\n```Bash\nrspec spec/foobar_spec.rb\n......\n114 example, 0 failures\n\nlib/foobar.rb new uncovered lines introduced (2 current vs 0 configured)\nUncovered lines:\nlib/foobar.rb:22\nlib/foobar.rb:23:6-19\n```\n\n - Missing coverage on every 💚 test run\n - Catch coverage issues before making PRs\n - Easily add coverage enforcement for legacy apps\n - 2-5% runtime overhead on small files, compared to 20% for `SimpleCov`\n - Branch coverage (disable via `branches: false`)\n - Use with [forking_test_runner](https://github.com/grosser/forking_test_runner) for exact per test coverage\n\n```Ruby\n# Gemfile\ngem 'single_cov', group: :test\n\n# spec/spec_helper.rb ... load single_cov before rails, libraries, minitest, or rspec\nrequire 'single_cov'\nSingleCov.setup :rspec # or :minitest\n\n# spec/foobar_spec.rb ... add covered! call to test files\nrequire 'spec_helper'\nSingleCov.covered!\n\ndescribe \"xyz\" do ...\n```\n\n### Missing target file\n\nEach `covered!` call expects to find a matching file, if it does not:\n\n```Ruby\n# change all guessed paths\nSingleCov.rewrite { |f| f.sub('lib/unit/', 'app/models/') }\n\n# mark directory as being in app and not lib\nSingleCov::RAILS_APP_FOLDERS \u003c\u003c 'presenters'\n\n# add 1-off\nSingleCov.covered! file: 'scripts/weird_thing.rb'\n```\n\n### Known uncovered\n\nAdd the inline comment `# uncovered` to ignore uncovered code.\n\nPrevent addition of new uncovered code, without having to cover all existing code by marking how many lines are uncovered:\n\n```Ruby\nSingleCov.covered! uncovered: 4\n```\n\n### Making a folder not get prefixed with lib/\n\nFor example packwerk components are hosted in `public` and not `lib/public`\n\n```ruby\nSingleCov::PREFIXES_TO_IGNORE \u003c\u003c \"public\"\n```\n\n### Missing coverage for implicit `else` in `if` or `case` statements\n\nWhen a report shows for example `1:14-16 # else`, that indicates that the implicit else is not covered.\n\n```ruby\n# needs 2 tests: one for `true` and one for `false`\nraise if a == b\n\n# needs 2 tests: one for `when b` and one for `else`\ncase a\nwhen b\nend\n```\n\n### Verify all code has tests \u0026 coverage\n\n```Ruby\n# spec/coverage_spec.rb\nSingleCov.not_covered! # not testing any code in lib/\n\ndescribe \"Coverage\" do\n  # recommended\n  it \"does not allow new tests without coverage check\" do\n    # option :tests to pass custom Dir.glob results\n    SingleCov.assert_used\n  end\n\n  # recommended\n  it \"does not allow new untested files\" do\n    # option :tests and :files to pass custom Dir.glob results\n    # :untested to get it passing with known untested files\n    SingleCov.assert_tested\n  end\n  \n  # optional for full coverage enforcement\n  it \"does not reduce full coverage\" do\n    # make sure that nobody adds `uncovered: 123` to any test that did not have it before\n    # option :tests to pass custom Dir.glob results\n    # option :currently_complete for expected list of full covered tests\n    # option :location for if you store that list in a separate file\n    SingleCov.assert_full_coverage currently_complete: [\"test/a_test.rb\"]\n  end\nend\n```\n\n### Automatic bootstrap\n\nRun this from `irb` to get SingleCov added to all test files.\n\n```Ruby\ntests = Dir['spec/**/*_spec.rb']\ncommand = \"bundle exec rspec %{file}\"\n\ntests.each do |f|\n  content = File.read(f)\n  next if content.include?('SingleCov.')\n\n  # add initial SingleCov call\n  content = content.split(/\\n/, -1)\n  insert = content.index { |l| l !~ /require/ \u0026\u0026 l !~ /^#/ }\n  content[insert...insert] = [\"\", \"SingleCov.covered!\"]\n  File.write(f, content.join(\"\\n\"))\n\n  # run the test to check coverage\n  result = `#{command.sub('%{file}', f)} 2\u003e\u00261`\n  if $?.success?\n    puts \"#{f} is good!\"\n    next\n  end\n\n  if uncovered = result[/\\((\\d+) current/, 1]\n    # configure uncovered\n    puts \"Uncovered for #{f} is #{uncovered}\"\n    content[insert+1] = \"SingleCov.covered! uncovered: #{uncovered}\"\n    File.write(f, content.join(\"\\n\"))\n  else\n    # mark bad tests for manual cleanup\n    content[insert+1] = \"# SingleCov.covered! # TODO: manually fix this\"\n    File.write(f, content.join(\"\\n\"))\n    puts \"Manually fix: #{f} ... output is:\\n#{result}\"\n  end\nend\n```\n\n### Cover multiple files from a single test\n\nWhen a single integration test covers multiple source files.\n\n```ruby\nSingleCov.covered! file: 'app/modes/user.rb'\nSingleCov.covered! file: 'app/mailers/user_mailer.rb'\nSingleCov.covered! file: 'app/controllers/user_controller.rb'\n```\n\n### Generating a coverage report\n\n```ruby\nSingleCov.coverage_report = \"coverage/.resultset.json\"\nSingleCov.coverage_report_lines = true # only report line coverage for coverage systems that do not support branch coverage\n```\n\nAuthor\n======\n[Michael Grosser](http://grosser.it)\u003cbr/\u003e\nmichael@grosser.it\u003cbr/\u003e\nLicense: MIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrosser%2Fsingle_cov","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgrosser%2Fsingle_cov","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrosser%2Fsingle_cov/lists"}