View Source Bond.Coverage (Bond v1.13.0)

Contract coverage: a test-time diagnostic that surfaces assertions which are checked but never observed to fail (#56).

The hardest contract bug to catch is the vacuous-but-passing assertion — one that runs at every call, always evaluates true, and so tells you nothing while looking like coverage. The compile-time assertion linter catches the structurally recognisable cases; this is the complementary dynamic signal: an assertion evaluated many times that has never once been false is a candidate for vacuity.

"Never failed" is a weak signal

A correct assertion over correct code, exercised by a passing suite, also never fails — that is what a green suite means. So ⚠ never failed is a prompt, not a verdict: either write a test that makes the assertion fail (proving it can — see the Writing Sound Assertions guide), or delete it. Coverage is an opt-in diagnostic you run deliberately, never an always-on warning.

Enabling

Coverage instrumentation is compile-time opt-in, so a build that does not enable it is byte-for-byte unchanged and pays nothing. Turn it on for the test environment:

# config/test.exs
config :bond, coverage: true

and print the report at the end of the suite by installing the reporter in test/test_helper.exs:

ExUnit.start()
Bond.Coverage.install_reporter()

Then mix test prints a per-assertion table of how many times each contract was checked and how many of those were failures. Recording itself needs no reporter — entries/0 and report/0 can be read at any point.

Summary

Types

One assertion's accumulated coverage: its identifying metadata plus how many times it was checked and how many of those checks failed.

Functions

Returns the accumulated coverage as a list of entry/0, sorted by module, then line.

Installs an ExUnit.after_suite/1 callback that prints report/0 once the suite finishes. Call this in test/test_helper.exs after ExUnit.start().

Records a single evaluation of the assertion described by assertion_info (the map the runtime check path already carries), classifying it as a failure when result is falsy.

Renders the accumulated coverage as a human-readable table, grouped by module and function. Assertions that were checked at least once but never failed are flagged ⚠ never failed.

Clears all recorded coverage.

Types

@type entry() :: %{
  assertion_id: term(),
  kind: atom(),
  label: term(),
  expression: String.t() | nil,
  module: module() | nil,
  function: term(),
  file: String.t() | nil,
  line: non_neg_integer() | nil,
  checked: non_neg_integer(),
  failed: non_neg_integer()
}

One assertion's accumulated coverage: its identifying metadata plus how many times it was checked and how many of those checks failed.

Functions

@spec entries() :: [entry()]

Returns the accumulated coverage as a list of entry/0, sorted by module, then line.

@spec install_reporter() :: :ok

Installs an ExUnit.after_suite/1 callback that prints report/0 once the suite finishes. Call this in test/test_helper.exs after ExUnit.start().

Link to this function

record(assertion_info, result)

View Source
@spec record(map(), term()) :: :ok

Records a single evaluation of the assertion described by assertion_info (the map the runtime check path already carries), classifying it as a failure when result is falsy.

Called from the coverage-recording variants of the runtime check helpers, which the compiler emits only when coverage is enabled. Cheap and concurrency-safe: one :ets.update_counter keyed by the assertion's stable :assertion_id.

@spec report() :: String.t()

Renders the accumulated coverage as a human-readable table, grouped by module and function. Assertions that were checked at least once but never failed are flagged ⚠ never failed.

@spec reset() :: :ok

Clears all recorded coverage.