runtimeanalyz.ing ● early access · sign-up open

← All capabilities

unit_test_candidates

CLI · MCP free

Test what matters. Untested methods ranked by how many places call them, so the most-depended-on code gets a test first. Aim your mutation runs there too.

In your app

Checkout
debit ( account, Account amount Money )
Finance::Accounts 14 callers
module Finance
  class Accounts
    def debit(account, amount) = …
  end
end
Refund
debit
Payroll
debit
SettlementJob + 10 more
debit

In your specs

any spec example
distance ∞
Finance::Accounts#debit never reached
First Finance::Accounts#debit fan-in 14 · no spec reaches it
Fan-in comes from the distinct callers in recorded runs, and distance from the spec-stamped call trees. The method everything leans on and no test looks at goes to the top.

The problem

You’ve inherited an app with patchy tests, or you’ve decided the suite needs to bite harder. Either way there’s more untested code than time, and the question is where to start.

A coverage report doesn’t answer it. It lists 140 uncovered methods, sorted by file name. A Report#footer_text nobody depends on sits at the same weight as this:

module Finance
  class Accounts
    def debit(account, amount)
      raise InsufficientFunds if account.balance < amount

      account.balance -= amount
      ledger.record(account, -amount)
    end
  end
end

Finance::Accounts#debit is called from checkout, refunds, payroll and four background jobs. If it’s wrong, everything that moves money is wrong. Coverage says it’s one red line among 140.

Mutation testing has the same blind spot from the other side. Mutant is slow, so running it everywhere evenly spends most of the time on code nothing leans on.

The fix

Rank the untested methods by how much depends on them:

$ ra unit_test_candidates app/

  fan-in  distance  method
      14         ∞  Finance::Accounts#debit
       9         4  Invoice#apply_discount
       6         ∞  Enrollment#withdraw
       1         ∞  Report#footer_text
· fan-in: distinct callers in recorded runs
· distance: fewest calls from a spec example to the method (∞: no spec reaches it)

debit has fourteen distinct callers and no spec reaches it at all, so it goes first. Ask how_to_reach for the setup and write the spec:

RSpec.describe Finance::Accounts do
  it "refuses a debit larger than the balance" do
    account = Account.new(balance: Money.new(500, "GBP"))

    expect { described_class.new.debit(account, Money.new(900, "GBP")) }
      .to raise_error(Finance::InsufficientFunds)
  end
end

Then point mutant at the top of the same list rather than the whole app.

How it works

  1. Fan-in. Every recorded call edge names its caller’s class and method. The number of distinct callers of a method, across your app runs and your specs, is its fan-in.
  2. Distance. Each call tree is stamped with the entry point that started it: a spec example or an app run. For every method, the shallowest depth at which a spec-stamped tree reaches it is its distance. A method only reached five calls deep, by a system spec, is barely tested. One no spec reaches is untested.
  3. Rank. High fan-in and long distance come first: the code most things rely on and the fewest tests look at directly.

It needs a recording of the code being used. Without tests, that’s your app running: in development, staging or production.

Limits

  • Reached isn’t asserted. A spec that calls debit and checks nothing still counts as reaching it. The distance tells you how close a test gets, not whether it checks the result.
  • Fan-in is callers, not importance. A method with one caller can still be the one that matters, like the only place you charge a card. Treat the ranking as an order to look in, not a verdict.
  • Only what ran. Callers in code paths you didn’t record don’t count towards fan-in.

how_to_reach

MCP free

Reach any method. The collaborators and call path that get an object into the state a method needs, taken from a real run. Setup for tests that use real objects instead of doubles.

See more →

testing_pyramid

CLI · MCP free

The boundaries your pyramid misses. Seams that are only ever crossed through a double, never for real, mapped onto your testing pyramid. Record production too and they're ranked by traffic.

See more →

how_far_it_reached

CLI · MCP free

Is your unit test a unit test? For each spec example, how many classes its real call tree touched and how deep it went. A unit spec that reaches thirty classes is telling you something.

See more →