Introduction to Test Coverage Reporting
Test coverage reporting is the systematic measurement and documentation of how thoroughly software testing exercises the application under test. It provides quantifiable metrics that answer critical questions: Which parts of the code have been tested? Which requirements have been verified? Which risks have been addressed? A comprehensive coverage report transforms abstract testing efforts into concrete, actionable data that drives decision-making and quality improvements.
Coverage reports serve multiple stakeholders—developers need code coverage insights, project managers require requirements traceability, and executives demand risk-based assurance. Effective coverage reporting bridges these needs with clear visualizations and meaningful metrics.
Types of Test Coverage
Code Coverage
Code coverage measures the extent to which source code is exercised by test execution:
Code Coverage Metrics:
Metric | Definition | Target Range | Use Case |
---|---|---|---|
Statement Coverage | % of code statements executed | 80-90% | Basic coverage baseline |
Branch Coverage | % of decision branches taken | 75-85% | Conditional logic validation |
Function Coverage | % of functions/methods called | 90-100% | API completeness check |
Line Coverage | % of code lines executed | 80-90% | Similar to statement coverage |
Condition Coverage | % of boolean sub-expressions evaluated | 70-80% | Complex logic testing |
Path Coverage | % of execution paths traversed | 60-70% | Critical flow validation |
Code Coverage Implementation:
# Python code coverage with pytest-cov
# pytest.ini configuration
[pytest]
addopts = --cov=src --cov-report=html --cov-report=term --cov-report=xml
# Run tests with coverage
# pytest --cov=myapp tests/
# Example test with coverage analysis
import pytest
from myapp.calculator import Calculator
class TestCalculator:
def test_addition(self):
calc = Calculator()
assert calc.add(2, 3) == 5
def test_division(self):
calc = Calculator()
assert calc.divide(10, 2) == 5
def test_division_by_zero(self):
calc = Calculator()
with pytest.raises(ZeroDivisionError):
calc.divide(10, 0)
def test_complex_calculation(self):
calc = Calculator()
# Tests branch coverage for multi-step operations
result = calc.calculate("(5 + 3) * 2")
assert result == 16
JavaScript/TypeScript Code Coverage:
// Jest configuration - jest.config.js
module.exports = {
collectCoverage: true,
coverageDirectory: 'coverage',
coverageReporters: ['text', 'html', 'lcov', 'json'],
coverageThreshold: {
global: {
statements: 80,
branches: 75,
functions: 80,
lines: 80
},
'./src/critical/': {
statements: 95,
branches: 90,
functions: 95,
lines: 95
}
},
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/**/*.test.{js,jsx,ts,tsx}',
'!src/**/index.{js,ts}'
]
};
// Example test with branch coverage
describe('UserAuthentication', () => {
it('should authenticate valid user', async () => {
const result = await authenticate('user@example.com', 'password123');
expect(result.success).toBe(true);
});
it('should reject invalid credentials', async () => {
const result = await authenticate('user@example.com', 'wrongpass');
expect(result.success).toBe(false);
expect(result.error).toBe('Invalid credentials');
});
it('should handle account lockout', async () => {
// Test branch coverage for security logic
for (let i = 0; i < 5; i++) {
await authenticate('user@example.com', 'wrongpass');
}
const result = await authenticate('user@example.com', 'password123');
expect(result.locked).toBe(true);
});
});
Requirements Coverage
Requirements coverage tracks which functional and non-functional requirements have been validated through testing:
Requirements Traceability Matrix:
Requirement ID | Description | Test Cases | Coverage Status | Priority |
---|---|---|---|---|
REQ-AUTH-001 | User login with email/password | TC-AUTH-001, TC-AUTH-002 | ✅ Covered | Critical |
REQ-AUTH-002 | Password reset flow | TC-AUTH-010, TC-AUTH-011 | ✅ Covered | High |
REQ-AUTH-003 | OAuth social login | TC-AUTH-020, TC-AUTH-021 | ⚠️ Partial | Medium |
REQ-PAY-001 | Credit card payment processing | TC-PAY-001 to TC-PAY-005 | ✅ Covered | Critical |
REQ-PAY-002 | PayPal integration | - | ❌ Not Covered | Medium |
REQ-PERF-001 | Page load < 2 seconds | TC-PERF-001 | ✅ Covered | High |
REQ-SEC-001 | SQL injection prevention | TC-SEC-001, TC-SEC-002 | ✅ Covered | Critical |
Automated Requirements Coverage Tracking:
# Requirements coverage analyzer
import json
from collections import defaultdict
class RequirementsCoverageAnalyzer:
def __init__(self, requirements_file, test_results_file):
self.requirements = self.load_requirements(requirements_file)
self.test_results = self.load_test_results(test_results_file)
def load_requirements(self, file_path):
with open(file_path, 'r') as f:
return json.load(f)
def load_test_results(self, file_path):
with open(file_path, 'r') as f:
return json.load(f)
def calculate_coverage(self):
coverage_map = defaultdict(lambda: {
'requirement': None,
'test_cases': [],
'status': 'Not Covered',
'priority': None
})
# Map requirements to test cases
for req in self.requirements:
req_id = req['id']
coverage_map[req_id]['requirement'] = req['description']
coverage_map[req_id]['priority'] = req['priority']
# Link test cases to requirements
for test in self.test_results:
for req_id in test.get('covers_requirements', []):
if req_id in coverage_map:
coverage_map[req_id]['test_cases'].append({
'id': test['id'],
'name': test['name'],
'status': test['status']
})
# Determine coverage status
for req_id, data in coverage_map.items():
if not data['test_cases']:
data['status'] = 'Not Covered'
elif all(tc['status'] == 'PASSED' for tc in data['test_cases']):
data['status'] = 'Covered'
elif any(tc['status'] == 'FAILED' for tc in data['test_cases']):
data['status'] = 'Failed'
else:
data['status'] = 'Partial'
return coverage_map
def generate_report(self):
coverage = self.calculate_coverage()
total_reqs = len(coverage)
covered_reqs = sum(1 for v in coverage.values() if v['status'] == 'Covered')
partial_reqs = sum(1 for v in coverage.values() if v['status'] == 'Partial')
not_covered_reqs = sum(1 for v in coverage.values() if v['status'] == 'Not Covered')
report = {
'summary': {
'total_requirements': total_reqs,
'covered': covered_reqs,
'partial': partial_reqs,
'not_covered': not_covered_reqs,
'coverage_percentage': (covered_reqs / total_reqs * 100) if total_reqs > 0 else 0
},
'details': coverage,
'gaps': [
{
'req_id': req_id,
'description': data['requirement'],
'priority': data['priority']
}
for req_id, data in coverage.items()
if data['status'] == 'Not Covered' and data['priority'] in ['Critical', 'High']
]
}
return report
# Example usage
analyzer = RequirementsCoverageAnalyzer('requirements.json', 'test_results.json')
coverage_report = analyzer.generate_report()
print(f"Requirements Coverage: {coverage_report['summary']['coverage_percentage']:.1f}%")
print(f"Critical/High gaps: {len(coverage_report['gaps'])}")
Risk Coverage
Risk coverage ensures that identified risks have corresponding test strategies and validation:
Risk-Based Testing Coverage:
# Risk coverage matrix
class RiskCoverageMatrix:
def __init__(self, risk_register, test_plan):
self.risks = risk_register
self.tests = test_plan
def analyze_risk_coverage(self):
risk_coverage = []
for risk in self.risks:
# Find tests addressing this risk
mitigating_tests = [
test for test in self.tests
if risk['id'] in test.get('mitigates_risks', [])
]
# Calculate risk coverage score
if not mitigating_tests:
coverage_score = 0
status = 'Not Covered'
else:
# Weight by test type and execution status
test_weight = {
'unit': 0.3,
'integration': 0.5,
'e2e': 0.8,
'manual': 0.6
}
total_weight = sum(
test_weight.get(test['type'], 0.5) *
(1.0 if test['status'] == 'PASSED' else 0.5)
for test in mitigating_tests
)
# Normalize to 0-100 scale
coverage_score = min(100, total_weight * 50)
if coverage_score >= 80:
status = 'Well Covered'
elif coverage_score >= 50:
status = 'Adequately Covered'
else:
status = 'Insufficiently Covered'
risk_coverage.append({
'risk_id': risk['id'],
'risk_title': risk['title'],
'risk_score': risk['risk_score'],
'risk_level': risk['level'],
'mitigating_tests': len(mitigating_tests),
'coverage_score': coverage_score,
'coverage_status': status
})
return risk_coverage
def identify_coverage_gaps(self, risk_coverage):
"""
Identify high-risk items with insufficient coverage
"""
gaps = [
item for item in risk_coverage
if item['risk_level'] in ['Critical', 'High']
and item['coverage_status'] in ['Not Covered', 'Insufficiently Covered']
]
# Sort by risk score (highest first)
gaps.sort(key=lambda x: x['risk_score'], reverse=True)
return gaps
# Example usage
rcm = RiskCoverageMatrix(risks, test_cases)
coverage = rcm.analyze_risk_coverage()
gaps = rcm.identify_coverage_gaps(coverage)
print(f"High-risk coverage gaps: {len(gaps)}")
for gap in gaps[:5]: # Top 5 gaps
print(f" {gap['risk_id']}: {gap['risk_title']} (Coverage: {gap['coverage_score']:.0f}%)")
Coverage Visualization Tools
Dashboard Design Principles
Effective coverage dashboards follow key design principles:
1. Multi-Level View:
- Executive summary (high-level metrics)
- Team view (actionable insights)
- Developer view (detailed code coverage)
2. Visual Hierarchy:
- Use color coding (red/yellow/green) for quick status recognition
- Prioritize critical information at the top
- Progressive disclosure for detailed data
3. Trend Analysis:
- Show coverage trends over time
- Highlight improvements and regressions
- Compare against targets
Interactive Coverage Dashboard
# Comprehensive coverage dashboard with Plotly
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
from datetime import datetime, timedelta
class CoverageDashboard:
def __init__(self, code_coverage, req_coverage, risk_coverage, historical_data):
self.code_cov = code_coverage
self.req_cov = req_coverage
self.risk_cov = risk_coverage
self.history = historical_data
def create_dashboard(self):
fig = make_subplots(
rows=3, cols=2,
subplot_titles=(
'Code Coverage Overview',
'Requirements Coverage Status',
'Coverage Trend (Last 30 Days)',
'Risk Coverage Distribution',
'Critical Gaps',
'Coverage by Module'
),
specs=[
[{'type': 'indicator'}, {'type': 'pie'}],
[{'type': 'scatter'}, {'type': 'bar'}],
[{'type': 'table'}, {'type': 'bar'}]
],
row_heights=[0.3, 0.35, 0.35]
)
# 1. Code coverage indicator
overall_coverage = self.code_cov['summary']['overall']
fig.add_trace(
go.Indicator(
mode="gauge+number+delta",
value=overall_coverage,
delta={'reference': 80, 'increasing': {'color': "green"}},
gauge={
'axis': {'range': [None, 100]},
'bar': {'color': self._get_color(overall_coverage)},
'steps': [
{'range': [0, 50], 'color': "lightgray"},
{'range': [50, 80], 'color': "lightyellow"},
{'range': [80, 100], 'color': "lightgreen"}
],
'threshold': {
'line': {'color': "red", 'width': 4},
'thickness': 0.75,
'value': 80
}
},
title={'text': "Overall Code Coverage"}
),
row=1, col=1
)
# 2. Requirements coverage pie chart
req_status = self.req_cov['summary']
fig.add_trace(
go.Pie(
labels=['Covered', 'Partial', 'Not Covered'],
values=[
req_status['covered'],
req_status['partial'],
req_status['not_covered']
],
marker=dict(colors=['#28a745', '#ffc107', '#dc3545']),
hole=0.4
),
row=1, col=2
)
# 3. Coverage trend
dates = [datetime.now() - timedelta(days=x) for x in range(30, 0, -1)]
fig.add_trace(
go.Scatter(
x=dates,
y=self.history['code_coverage'],
mode='lines+markers',
name='Code Coverage',
line=dict(color='#007bff', width=3)
),
row=2, col=1
)
fig.add_trace(
go.Scatter(
x=dates,
y=self.history['req_coverage'],
mode='lines+markers',
name='Requirements Coverage',
line=dict(color='#28a745', width=3)
),
row=2, col=1
)
# Add target line
fig.add_hline(y=80, line_dash="dash", line_color="red",
annotation_text="Target: 80%", row=2, col=1)
# 4. Risk coverage distribution
risk_dist = pd.DataFrame(self.risk_cov).groupby('coverage_status').size()
fig.add_trace(
go.Bar(
x=risk_dist.index,
y=risk_dist.values,
marker=dict(color=['#28a745', '#ffc107', '#dc3545'])
),
row=2, col=2
)
# 5. Critical gaps table
gaps = self._get_critical_gaps()
fig.add_trace(
go.Table(
header=dict(
values=['Type', 'ID', 'Description', 'Priority'],
fill_color='paleturquoise',
align='left'
),
cells=dict(
values=[
gaps['type'],
gaps['id'],
gaps['description'],
gaps['priority']
],
fill_color='lavender',
align='left'
)
),
row=3, col=1
)
# 6. Coverage by module
modules = list(self.code_cov['by_module'].keys())
coverage_values = list(self.code_cov['by_module'].values())
fig.add_trace(
go.Bar(
x=modules,
y=coverage_values,
marker=dict(
color=coverage_values,
colorscale='RdYlGn',
cmin=0,
cmax=100,
showscale=True
),
text=[f"{v:.1f}%" for v in coverage_values],
textposition='outside'
),
row=3, col=2
)
# Update layout
fig.update_layout(
title_text="Test Coverage Dashboard",
showlegend=True,
height=1200,
hovermode='closest'
)
return fig
def _get_color(self, coverage):
if coverage >= 80:
return "#28a745" # Green
elif coverage >= 50:
return "#ffc107" # Yellow
else:
return "#dc3545" # Red
def _get_critical_gaps(self):
"""Extract critical coverage gaps"""
gaps = {
'type': [],
'id': [],
'description': [],
'priority': []
}
# Add requirement gaps
for gap in self.req_cov.get('gaps', [])[:3]:
gaps['type'].append('Requirement')
gaps['id'].append(gap['req_id'])
gaps['description'].append(gap['description'][:50] + '...')
gaps['priority'].append(gap['priority'])
# Add risk gaps
for item in self.risk_cov[:3]:
if item['coverage_status'] == 'Not Covered':
gaps['type'].append('Risk')
gaps['id'].append(item['risk_id'])
gaps['description'].append(item['risk_title'][:50] + '...')
gaps['priority'].append(item['risk_level'])
return gaps
# Generate dashboard
dashboard = CoverageDashboard(code_cov_data, req_cov_data, risk_cov_data, historical_trends)
fig = dashboard.create_dashboard()
fig.write_html('coverage_dashboard.html')
fig.show()
Coverage Heatmaps
Heatmaps provide intuitive visualization of coverage distribution:
# Coverage heatmap for module-level analysis
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
def create_coverage_heatmap(coverage_data):
"""
Create heatmap showing coverage across modules and coverage types
"""
# Prepare data matrix
modules = list(coverage_data.keys())
metrics = ['Statement', 'Branch', 'Function', 'Line']
data_matrix = []
for module in modules:
row = [
coverage_data[module].get('statement', 0),
coverage_data[module].get('branch', 0),
coverage_data[module].get('function', 0),
coverage_data[module].get('line', 0)
]
data_matrix.append(row)
data_matrix = np.array(data_matrix)
# Create heatmap
plt.figure(figsize=(12, 8))
sns.heatmap(
data_matrix,
annot=True,
fmt='.1f',
cmap='RdYlGn',
xticklabels=metrics,
yticklabels=modules,
vmin=0,
vmax=100,
cbar_kws={'label': 'Coverage %'}
)
plt.title('Code Coverage Heatmap by Module', fontsize=16, fontweight='bold')
plt.xlabel('Coverage Metric', fontsize=12)
plt.ylabel('Module', fontsize=12)
plt.tight_layout()
return plt
# Example usage
coverage_by_module = {
'Authentication': {'statement': 92, 'branch': 85, 'function': 95, 'line': 90},
'Payment': {'statement': 88, 'branch': 80, 'function': 90, 'line': 87},
'UserProfile': {'statement': 75, 'branch': 70, 'function': 80, 'line': 74},
'Dashboard': {'statement': 65, 'branch': 60, 'function': 70, 'line': 64},
'Reporting': {'statement': 45, 'branch': 40, 'function': 50, 'line': 43}
}
heatmap = create_coverage_heatmap(coverage_by_module)
heatmap.savefig('coverage_heatmap.png', dpi=300, bbox_inches='tight')
Coverage Metrics and KPIs
Key Performance Indicators
Essential Coverage KPIs:
KPI | Formula | Target | Interpretation |
---|---|---|---|
Overall Coverage | (Covered Items / Total Items) × 100 | ≥80% | General testing completeness |
Critical Path Coverage | (Critical Paths Tested / Total Critical Paths) × 100 | 100% | Core functionality assurance |
Defect Detection Coverage | Defects Found / (Defects Found + Defects Escaped) × 100 | ≥90% | Testing effectiveness |
Requirements Coverage | (Verified Requirements / Total Requirements) × 100 | ≥95% | Traceability completeness |
Risk Coverage Index | Σ(Risk Score × Coverage %) / Σ(Risk Score) | ≥80% | Risk mitigation effectiveness |
Coverage Growth Rate | (Current Coverage - Previous Coverage) / Previous Coverage × 100 | >0% | Continuous improvement |
Weighted Coverage Score
# Calculate weighted coverage score based on criticality
class WeightedCoverageCalculator:
def __init__(self, coverage_data, weights):
self.coverage = coverage_data
self.weights = weights
def calculate_weighted_score(self):
"""
Calculate overall coverage score with weighted components
"""
weighted_sum = 0
total_weight = 0
for component, data in self.coverage.items():
weight = self.weights.get(component, 1.0)
coverage_pct = data['coverage_percentage']
weighted_sum += coverage_pct * weight
total_weight += weight
weighted_score = weighted_sum / total_weight if total_weight > 0 else 0
return {
'weighted_score': weighted_score,
'components': {
comp: {
'coverage': data['coverage_percentage'],
'weight': self.weights.get(comp, 1.0),
'contribution': data['coverage_percentage'] * self.weights.get(comp, 1.0)
}
for comp, data in self.coverage.items()
}
}
# Example usage
coverage_components = {
'code_coverage': {'coverage_percentage': 85.0},
'requirements_coverage': {'coverage_percentage': 92.0},
'risk_coverage': {'coverage_percentage': 78.0},
'api_coverage': {'coverage_percentage': 88.0},
'ui_coverage': {'coverage_percentage': 75.0}
}
weights = {
'code_coverage': 1.0,
'requirements_coverage': 2.0, # Higher priority
'risk_coverage': 2.5, # Highest priority
'api_coverage': 1.5,
'ui_coverage': 1.0
}
calculator = WeightedCoverageCalculator(coverage_components, weights)
result = calculator.calculate_weighted_score()
print(f"Weighted Coverage Score: {result['weighted_score']:.1f}%")
print("\nComponent Contributions:")
for comp, details in result['components'].items():
print(f" {comp}: {details['coverage']:.1f}% × {details['weight']} = {details['contribution']:.1f}")
Automated Coverage Reporting
CI/CD Integration
Integrate coverage reporting into continuous integration pipelines:
# GitHub Actions workflow for coverage reporting
name: Test Coverage Report
on:
pull_request:
branches: [main, develop]
push:
branches: [main]
jobs:
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pytest pytest-cov
- name: Run tests with coverage
run: |
pytest --cov=src --cov-report=xml --cov-report=html --cov-report=term
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
fail_ci_if_error: true
- name: Coverage comment
uses: py-cov-action/python-coverage-comment-action@v3
with:
GITHUB_TOKEN: ${{ github.token }}
MINIMUM_GREEN: 80
MINIMUM_ORANGE: 70
- name: Check coverage threshold
run: |
python scripts/check_coverage_threshold.py --threshold 80
- name: Generate coverage badge
run: |
coverage-badge -o coverage.svg -f
- name: Archive coverage artifacts
uses: actions/upload-artifact@v3
with:
name: coverage-report
path: htmlcov/
Coverage Threshold Enforcement
# Coverage threshold enforcement script
import json
import sys
from pathlib import Path
class CoverageThresholdEnforcer:
def __init__(self, coverage_file, thresholds):
self.coverage_data = self._load_coverage(coverage_file)
self.thresholds = thresholds
def _load_coverage(self, file_path):
with open(file_path, 'r') as f:
return json.load(f)
def check_thresholds(self):
violations = []
# Check overall coverage
overall = self.coverage_data['totals']['percent_covered']
if overall < self.thresholds['overall']:
violations.append({
'type': 'Overall Coverage',
'actual': overall,
'threshold': self.thresholds['overall'],
'deficit': self.thresholds['overall'] - overall
})
# Check per-file coverage
for file_path, data in self.coverage_data['files'].items():
file_coverage = data['summary']['percent_covered']
# Critical files have stricter thresholds
if self._is_critical_file(file_path):
threshold = self.thresholds.get('critical_files', 95)
else:
threshold = self.thresholds.get('per_file', 70)
if file_coverage < threshold:
violations.append({
'type': 'File Coverage',
'file': file_path,
'actual': file_coverage,
'threshold': threshold,
'deficit': threshold - file_coverage
})
return violations
def _is_critical_file(self, file_path):
critical_patterns = ['auth', 'payment', 'security', 'core']
return any(pattern in file_path.lower() for pattern in critical_patterns)
def report_violations(self, violations):
if not violations:
print("✅ All coverage thresholds met!")
return True
print("❌ Coverage threshold violations found:\n")
for v in violations:
if v['type'] == 'Overall Coverage':
print(f" Overall: {v['actual']:.1f}% (threshold: {v['threshold']}%, deficit: {v['deficit']:.1f}%)")
else:
print(f" {v['file']}: {v['actual']:.1f}% (threshold: {v['threshold']}%, deficit: {v['deficit']:.1f}%)")
return False
# Usage in CI/CD
if __name__ == '__main__':
thresholds = {
'overall': 80,
'critical_files': 95,
'per_file': 70
}
enforcer = CoverageThresholdEnforcer('coverage.json', thresholds)
violations = enforcer.check_thresholds()
success = enforcer.report_violations(violations)
sys.exit(0 if success else 1)
Coverage Report Best Practices
Actionable Reporting
Do’s:
- Highlight Gaps: Emphasize what’s NOT covered, not just what is
- Provide Context: Explain why certain coverage levels are acceptable
- Trend Analysis: Show coverage evolution over time
- Prioritize: Focus on critical/high-risk areas first
- Link to Action: Every gap should have a mitigation plan
Don’ts:
- Don’t chase 100%: Diminishing returns beyond 85-90%
- Don’t ignore quality: High coverage ≠ good tests
- Don’t report in isolation: Combine multiple coverage types
- Don’t hide bad news: Transparency builds trust
- Don’t set arbitrary targets: Base thresholds on risk and criticality
Coverage Anti-Patterns
Anti-Pattern | Problem | Solution |
---|---|---|
Vanity Metrics | High coverage with poor assertions | Review test quality, not just quantity |
Coverage Theater | Writing tests just to increase % | Focus on meaningful test scenarios |
Ignored Gaps | Known gaps never addressed | Formal gap closure tracking |
Static Targets | Same threshold for all code | Risk-based, component-specific targets |
Report Fatigue | Too many reports, no action | Single consolidated actionable dashboard |
Conclusion
Test coverage reporting transforms abstract testing efforts into concrete, measurable outcomes that drive quality improvements and informed decision-making. By combining code coverage, requirements traceability, and risk-based analysis with powerful visualization tools, QA teams create comprehensive coverage insights that serve all stakeholders.
The most effective coverage reports go beyond simple percentages—they tell a story of testing effectiveness, highlight critical gaps, track improvement trends, and provide actionable recommendations. When integrated into CI/CD pipelines with automated enforcement, coverage reporting becomes a continuous quality feedback loop that prevents regressions and ensures consistent quality standards.
Remember: Coverage is a means to an end, not the end itself. The ultimate goal is not perfect coverage metrics but confidence that the software works as intended, risks are mitigated, and quality standards are met. Use coverage reports as a tool for continuous improvement, not as a scorecard for judgment.