PostgreSQL test runner and coverage tool
pgcov is a Go-based CLI tool that discovers *_test.sql files, instruments SQL/PL/pgSQL source code for coverage tracking, executes tests in isolated temporary databases, and generates coverage reports in JSON and LCOV formats.
- 🧪 Automatic Test Discovery: Finds
*_test.sqlfiles and co-located source files - 🔒 Complete Test Isolation: Each test runs in a temporary database
- 📊 Coverage Tracking: Statement-level coverage via SQL instrumentation
- 📈 Multiple Report Formats: JSON and LCOV output for CI/CD integration
- ⚡ Parallel Execution: Optional concurrent test execution with
--parallelflag - 🎯 PostgreSQL Native: Direct protocol access via pgx, no external dependencies
- Go: 1.21 or later (for building)
- C Compiler: Required for CGO (GCC on Linux, MinGW-w64 on Windows)
- PostgreSQL: 13 or later (running and accessible)
- Permissions: CREATEDB privilege for test isolation
Linux/macOS:
# Ubuntu/Debian
sudo apt-get install build-essential
# macOS (Xcode Command Line Tools)
xcode-select --install
# Fedora/RHEL
sudo dnf install gccWindows:
-
Install MSYS2
-
Open MSYS2 terminal and run:
pacman -S mingw-w64-x86_64-gcc
-
Add
C:\msys64\mingw64\binto your PATH
Linux/macOS:
# Clone repository
git clone https://github.com/cybertec-postgresql/pgcov.git
cd pgcov
# Enable CGO and build
export CGO_ENABLED=1
go build -o pgcov ./cmd/pgcov
# (Optional) Install to PATH
go install ./cmd/pgcovWindows (PowerShell) - Manual Build:
# Clone repository
git clone https://github.com/cybertec-postgresql/pgcov.git
cd pgcov
# Enable CGO and set compiler
$env:CGO_ENABLED = "1"
$env:CC = "C:\msys64\mingw64\bin\gcc.exe"
$env:PATH = "$env:PATH;C:\msys64\mingw64\bin"
# Build
go build -o pgcov.exe .\cmd\pgcovpgcov uses pg_query_go which wraps the PostgreSQL query parser (libpg_query) written in C. This provides native PostgreSQL SQL parsing capabilities but requires CGO to be enabled during compilation.
export PGHOST=localhost
export PGPORT=5432
export PGUSER=postgres
export PGPASSWORD=yourpassword
export PGDATABASE=postgresTest files must match *_test.sql pattern and be co-located with source files:
myproject/
├── auth/
│ ├── authenticate.sql # Source (will be instrumented)
│ └── auth_test.sql # Test
# Current directory
pgcov run .
# Recursive (Go-style)
pgcov run ./...
# Specific directory
pgcov run ./tests/# HTML format (human-readable)
pgcov report --format=html -o coverage.html
# LCOV format (for CI)
pgcov report --format=lcov -o coverage.lcov# Run tests and collect coverage
pgcov run [path]
# Generate coverage report
pgcov report [--format=json|lcov|html] [-o output-file]
# Show help
pgcov help [command]
# Show version
pgcov --versionConnection:
--host: PostgreSQL host (default:localhost)--port: PostgreSQL port (default:5432, valid range: 1-65535)--user: PostgreSQL user (default: current user)--password: PostgreSQL password--database: Template database (default:postgres)
Execution:
--timeout: Per-test timeout (default:30s, format:10s,1m,90s)--parallel: Concurrent tests (default:1, valid range: 1-100)--verbose: Enable debug output
Output:
--coverage-file: Coverage data path (default:.pgcov/coverage.json)
pgcov respects standard PostgreSQL environment variables:
PGHOST,PGPORT,PGUSER,PGPASSWORD,PGDATABASE
Configuration Priority (highest to lowest):
- Command-line flags (e.g.,
--host) - Environment variables (e.g.,
PGHOST) - Default values
pgcov validates all configuration values and provides helpful error messages:
# Invalid port
$ pgcov run --port=99999 .
Error: configuration error for port: invalid port number: 99999
Suggestion: Port must be between 1 and 65535. Default PostgreSQL port is 5432.
Set via --port flag or PGPORT environment variable.
# Invalid parallelism
$ pgcov run --parallel=0 .
Error: configuration error for parallel: parallelism must be at least 1, got: 0
Suggestion: Use --parallel=N where N is number of tests to run concurrently.
Use 1 for sequential execution.
# Invalid timeout
$ pgcov run --timeout=-5s .
Error: configuration error for timeout: timeout must be positive
Suggestion: Use --timeout flag with format like '30s', '1m', '90s'. Default is 30s.-- auth_test.sql
-- Setup: Create schema and test data
CREATE TABLE users (id INT PRIMARY KEY, name TEXT);
INSERT INTO users VALUES (1, 'Alice'), (2, 'Bob');
-- Test: Verify behavior
DO $$
BEGIN
IF NOT authenticate(1) THEN
RAISE EXCEPTION 'Test failed: User 1 should authenticate';
END IF;
IF authenticate(999) THEN
RAISE EXCEPTION 'Test failed: Invalid user should not authenticate';
END IF;
RAISE NOTICE 'All tests passed';
END;
$$;Source files in the same directory as test files will be automatically instrumented:
-- authenticate.sql
CREATE OR REPLACE FUNCTION authenticate(user_id INT) RETURNS BOOLEAN AS $$
BEGIN
RETURN EXISTS(SELECT 1 FROM users WHERE id = user_id);
END;
$$ LANGUAGE plpgsql;name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
ports:
- 5432:5432
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Install pgcov
run: go install github.com/cybertec-postgresql/pgcov/cmd/pgcov@latest
- name: Run tests
env:
PGHOST: localhost
PGPORT: 5432
PGUSER: postgres
PGPASSWORD: postgres
run: pgcov run ./...
- name: Generate LCOV report
run: pgcov report --format=lcov -o coverage.lcov
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: coverage.lcov- CLI Layer: Command routing and user interface (
urfave/cli/v3) - Discovery Layer: Test and source file discovery (filesystem traversal)
- Parser Layer: SQL parsing and AST access (
pg_query_go) - Instrumentation Layer: AST rewriting with coverage injection
- Database Layer: PostgreSQL connections and temporary databases (
pgx/v5) - Runner Layer: Test execution orchestration and isolation
- Coverage Layer: Signal collection and aggregation (LISTEN/NOTIFY)
- Reporter Layer: Output formatting (HTML, JSON, LCOV)
The project includes comprehensive integration tests that use testcontainers to spin up a PostgreSQL instance.
Linux/macOS:
# Enable CGO
export CGO_ENABLED=1
# Run all tests
go test ./...
# Run with verbose output
go test -v ./...
# Run specific test
go test -v ./internal -run TestEndToEndWithTestcontainers
# Run with timeout (useful for integration tests)
go test -timeout 5m ./...
# Run tests with coverage
go test -cover ./...Windows (PowerShell):
# Enable CGO and set compiler
$env:CGO_ENABLED = "1"
$env:CC = "C:\msys64\mingw64\bin\gcc.exe"
$env:PATH = "$env:PATH;C:\msys64\mingw64\bin"
# Run all tests
go test .\...
# Run with verbose output
go test -v .\...
# Run specific test
go test -v .\internal -run TestEndToEndWithTestcontainers
# Run with timeout
go test -timeout 5m .\...
# Run tests with coverage
go test -cover .\...Linux/macOS:
# Development build
export CGO_ENABLED=1
go build -o pgcov ./cmd/pgcov
# Release build with optimizations
go build -ldflags="-s -w" -o pgcov ./cmd/pgcov
# Format code
go fmt ./...
# Lint
go vet ./...
# Clean build cache
go clean -cacheWindows (PowerShell):
# Development build
$env:CGO_ENABLED = "1"
$env:CC = "C:\msys64\mingw64\bin\gcc.exe"
$env:PATH = "$env:PATH;C:\msys64\mingw64\bin"
go build -o pgcov.exe .\cmd\pgcov
# Release build with optimizations
go build -ldflags="-s -w" -o pgcov.exe .\cmd\pgcov
# Format code
go fmt .\...
# Lint
go vet .\...
# Clean build cache
go clean -cacheDocker: Integration tests use testcontainers-go which requires Docker to be running:
- Linux: Docker Engine
- macOS: Docker Desktop
- Windows: Docker Desktop with WSL2 backend
PostgreSQL Version: Tests verify PostgreSQL 13+ compatibility using the postgres:16-alpine image.
CGO errors on Linux:
# Install build tools
sudo apt-get update
sudo apt-get install build-essential
# Verify GCC is available
gcc --versionCGO errors on Windows:
# Verify GCC is in PATH
gcc --version
# If not found, ensure MSYS2 MinGW64 is in PATH:
$env:PATH = "$env:PATH;C:\msys64\mingw64\bin"Missing DLL errors on Windows:
Ensure C:\msys64\mingw64\bin is in your PATH to access required MinGW DLLs.
Test container startup failures:
# Verify Docker is running
docker ps
# Pull PostgreSQL image manually
docker pull postgres:16-alpineThis project includes complete VS Code configuration for CGO development.
- ✅ Automatic CGO Environment - No manual env var setup required
- ✅ IntelliSense Support - Full code completion for CGO code
- ✅ Debug Configurations - F5 to debug, with 5 pre-configured scenarios
- ✅ Build Tasks - Ctrl+Shift+B to build, plus 9 other tasks
- ✅ Integrated Terminal - CGO variables automatically set
- ✅ Cross-Platform - Windows, Linux, and macOS configurations
-
Open workspace in VS Code
code . -
Reload window (if already open)
- Press
Ctrl+Shift+P(orCmd+Shift+Pon macOS) - Type "Reload Window"
- Press Enter
- Press
-
Verify gopls is working
- Check bottom-right status bar
- Should show "gopls" without errors
-
Build the project
- Press
Ctrl+Shift+B(orCmd+Shift+Bon macOS) - Or: Terminal → Run Build Task
- Press
-
Debug the project
- Open Run and Debug sidebar (
Ctrl+Shift+D) - Select "Launch pgcov"
- Press
F5
- Open Run and Debug sidebar (
See .vscode/README.md for detailed documentation:
- settings.json - CGO environment for Go tools and terminal
- launch.json - Debug configurations
- tasks.json - Build and test tasks
MIT
Contributions welcome! Please open an issue or pull request.
- Documentation: Full docs
- Issues: GitHub Issues
- Examples: Examples directory