.sql files are no exception. Two
mainstream stacks produce real coverage for SQL sources:
Both need one deliberate step in the pipeline before mehen sees usable data. This page covers
those two steps; the metric semantics live on the
line, branch, and
function pages.
What to expect from SQL coverage
Oracle’s instrumentation (DBMS_PLSQL_CODE_COVERAGE, which utPLSQL drives) reports lines
only: utPLSQL’s Cobertura output marks every line branch="false" and emits no <method>
elements. A measured .sql file therefore publishes coverage.line, coverage.line.covered,
and coverage.line.total — and nothing else. coverage.branch and coverage.function stay
absent, not zero: a gate on an unmeasured dimension skips rather than fails, per the
measured-or-absent rule. SQLCover reports statement coverage,
which likewise lands on the line dimension after the Cobertura transform.
Coverage attaches at two granularities for SQL. The file root carries the totals, and every
routine — a standalone CREATE FUNCTION/PROCEDURE/TRIGGER, a routine inside a package or
type body, or a subprogram declared in another routine’s DECLARE section — is a function space
in the metric tree, nested under its statement’s span. Each routine space
receives coverage.line (and coverage.branch, when measured) scoped to its own line range,
exactly like functions in any other language: a package body at 50% stops being one opaque
number and becomes “get_a 100%, set_b 0%”. Statement spaces themselves carry no
coverage keys — the routine is the meaningful unit, and it is the granularity the planned CRAP
composite consumes. top-offenders ranks files, so gates stay
file-level; the per-routine values live on the spaces in the JSON report.
utPLSQL: map database objects to file names
This is the step most pipelines miss. Run without file mapping, utPLSQL writes database object identifiers where Cobertura expects file paths:hr.betwnstr is schema.unit from the Oracle dictionary. No spelling of a repository path ends
in it, so path matching correctly refuses to attribute the
data — every .sql file reports “unmeasured”, and mehen metrics -v logs that the report’s
entries matched nothing.
utPLSQL’s fix is project-based coverage:
hand utPLSQL-cli your source tree with -source_path, and it maps database objects back to the
files that created them, writing repository paths into the report:
cobertura.xml (or coverage.xml) also makes it eligible for
auto-discovery, so bare --coverage works too.
SQLCover: transform OpenCover output to Cobertura
SQLCover (the coverage layer usually paired with tSQLt) emits OpenCover-format XML, which is not one of mehen’s ingested formats. The standard .NET bridge closes the gap: ReportGenerator converts OpenCover to Cobertura. Add one step between test run and mehen:[dbo].[betwnstr], and the paths that reach the Cobertura output
depend on how your deployment scripts fed SQLCover. Inspect one filename attribute from the
transformed report and confirm a repository path can end with it before wiring a gate — the
path-matching page explains exactly which spellings
reconcile.
Gating
Once report entries carry repository paths, SQL files participate in coverage thresholds and ranking like any other language:References
- utPLSQL user guide: Code coverage — project-based coverage, file mapping parameters, and object-file mapping rules. utPLSQL project.
- utPLSQL-cli README —
-source_path,-regex_expression, and reporter options. utPLSQL project. - DBMS_PLSQL_CODE_COVERAGE — the Oracle-supplied instrumentation utPLSQL drives (basic-block granularity). Oracle Database documentation.
- SQLCover — coverage collection for SQL Server, OpenCover output. Ed Elliott.
- tSQLt — the SQL Server unit-testing framework SQLCover pairs with.
- ReportGenerator — OpenCover-to-Cobertura
conversion (
-reporttypes:Cobertura). Daniel Palme.