Skip to content

test(metrics): eliminate coverage theater with exact value assertions#147

Merged
rjmurillo merged 6 commits intomainfrom
test/eliminate-coverage-theater
Feb 15, 2026
Merged

test(metrics): eliminate coverage theater with exact value assertions#147
rjmurillo merged 6 commits intomainfrom
test/eliminate-coverage-theater

Conversation

@rjmurillo
Copy link
Owner

@rjmurillo rjmurillo commented Feb 15, 2026

Summary

Replaces coverage theater tests (tests that verify something was recorded, not that the right thing was recorded) with exact value assertions. A broken implementation that counts hits as misses, leaks tags between caches, or increments on Set instead of TryGetValue would have passed the previous test suite.

Changes

Strengthened Assertions (8 tests)

Test Before After
MultipleNamedCaches Assert.NotEmpty Exact per-cache hit/miss counts
WithCacheName Assert.Contains(true, ...) ALL measurements have correct tag
EstimatedSize Assert.NotEmpty Exact value = 42
EvictionAfterDisposal No counter assertion Counter NOT incremented post-dispose
MeterFactory Operations work Metrics emitted through factory meter
AdditionalTags (2 tests) No throw Tag contents verified

Infrastructure

  • Extract MetricCollectionHarness to Tests/Unit/Shared/MetricCollectionHarness.cs (was duplicated in 2 files)
  • Create OTelTestHelper.cs for InMemoryExporter-based unit test assertions
  • Add OpenTelemetry.Exporter.InMemory to Unit.csproj

Impact

  • Net -417 lines (deduplication + stronger assertions)
  • All 226 tests pass (208 unit + 18 integration)
  • Zero build warnings

- Extract MetricCollectionHarness to shared file (remove 2 duplicate copies)
- Create OTelTestHelper for InMemoryExporter-based unit tests
- Strengthen MultipleNamedCaches: assert exact per-cache hit/miss counts
- Strengthen WithCacheName: assert ALL measurements have correct tag
- Strengthen EstimatedSize: assert exact value (42) not just non-empty
- Strengthen EvictionAfterDisposal: assert counter NOT incremented post-dispose
- Strengthen MeterFactory: verify metrics emitted through factory meter
- Strengthen AdditionalTags: assert tag contents (whitespace excluded, duplicates excluded)
- Add OpenTelemetry.Exporter.InMemory to Unit.csproj
- Net -417 lines (deduplication + stronger assertions)
Copilot AI review requested due to automatic review settings February 15, 2026 06:30
@gemini-code-assist
Copy link

Summary of Changes

Hello @rjmurillo, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the reliability and accuracy of metric-related unit tests by moving from "coverage theater" to exact value assertions. It refactors the test infrastructure by centralizing metric collection utilities and introducing OpenTelemetry-specific test helpers, leading to more robust validation of metric emissions and a cleaner codebase.

Highlights

  • Enhanced Metric Test Assertions: Replaced vague Assert.NotEmpty or Assert.Contains(true, ...) with precise, exact value assertions across 8 metric-related tests, ensuring correct metric emission and tagging.
  • Test Infrastructure Refactoring: Extracted the MetricCollectionHarness into a shared utility file to eliminate duplication and improve maintainability.
  • OpenTelemetry Test Helpers: Introduced a new OTelTestHelper class to simplify OpenTelemetry metric assertions using an InMemoryExporter.
  • Dependency Update: Added OpenTelemetry.Exporter.InMemory and related packages to the unit test project.
  • Code Reduction: Achieved a net reduction of 417 lines of code through deduplication and more efficient testing patterns.
Changelog
  • tests/Unit/CoverageGapTests.cs
    • Updated MeteredMemoryCache_WithTrackStatistics_ReportsEstimatedSize to assert an exact estimated size of 42.
    • Modified MeteredMemoryCache_NonNullMeterFactory_UsesFactoryMeter to perform multiple cache operations (miss, set, hit) and assert exact hit/miss counts.
    • Enhanced MeteredMemoryCache_EvictionCallback_AfterDisposal_NoMetricUpdate to use a MeterListener and assert that eviction counts do not increase after cache disposal.
    • Updated MeteredMemoryCache_AdditionalTags_EmptyKeyAfterTrim_Excluded and MeteredMemoryCache_AdditionalTags_CacheNameDuplicate_Excluded to verify exact tag contents and exclusion rules using a MeterListener.
  • tests/Unit/MetricEmissionAccuracyTests.cs
    • Removed the duplicated MetricCollectionHarness and MetricMeasurement nested classes.
    • Added a using Unit.Shared; directive to reference the now-shared utility.
  • tests/Unit/Shared/MeteredCacheTestBase.cs
    • Removed the duplicated MetricCollectionHarness and MetricMeasurement nested classes.
    • Updated WithCacheName_EmitsCacheNameTag to assert that all measurements have the correct cache.name tag using AssertAllMeasurementsHaveTags.
    • Modified MultipleNamedCaches_EmitSeparateMetrics to assert exact hit and miss counts for each named cache using GetAggregatedCount.
  • tests/Unit/Shared/MetricCollectionHarness.cs
    • Added a new file containing the MetricCollectionHarness and MetricMeasurement classes, extracted from MetricEmissionAccuracyTests.cs and MeteredCacheTestBase.cs.
  • tests/Unit/Shared/OTelTestHelper.cs
    • Added a new file providing static helper methods for OpenTelemetry metric testing, including CreateMeterProvider, FindMetric, GetMetricValueByTag, AssertMetricValueByTag, and AssertAllPointsHaveTag.
  • tests/Unit/Unit.csproj
    • Added OpenTelemetry, OpenTelemetry.Exporter.InMemory, and OpenTelemetry.Extensions.Hosting NuGet package references.
Activity
  • No specific activity (comments, reviews, or progress updates) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This is an excellent pull request that significantly improves the quality and reliability of the metric tests. Replacing "coverage theater" tests with assertions for exact values is a fantastic step towards ensuring correctness. The refactoring to deduplicate MetricCollectionHarness and introduce OTelTestHelper also greatly improves the test infrastructure's maintainability.

I've found a few areas in the new helper classes where minor improvements can be made for efficiency and correctness. My comments are focused on these new shared components.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR significantly improves test quality by replacing weak "coverage theater" assertions with exact value assertions. Coverage theater refers to tests that verify something was recorded but not that the right thing was recorded - for example, checking that metrics are non-empty rather than verifying exact hit/miss counts. The PR also eliminates code duplication by extracting MetricCollectionHarness to a shared location.

Changes:

  • Strengthened 8 metric tests with exact value assertions instead of existence checks
  • Extracted duplicated MetricCollectionHarness class to tests/Unit/Shared/
  • Added OTelTestHelper.cs with OpenTelemetry InMemoryExporter utilities (unused in this PR)
  • Added OpenTelemetry NuGet packages to support future metric testing patterns

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/Unit/Unit.csproj Added OpenTelemetry packages for InMemoryExporter-based testing
tests/Unit/Shared/OTelTestHelper.cs New helper class for OpenTelemetry metric assertions (not used yet)
tests/Unit/Shared/MetricCollectionHarness.cs Extracted from duplicated implementations, now shared
tests/Unit/Shared/MeteredCacheTestBase.cs Removed duplicated harness, strengthened assertions in WithCacheName and MultipleNamedCaches tests
tests/Unit/MetricEmissionAccuracyTests.cs Removed duplicated harness, now uses shared version
tests/Unit/CoverageGapTests.cs Strengthened assertions in 4 tests: EstimatedSize (exact value), EvictionAfterDisposal (counter not incremented), AdditionalTags tests (verify tag contents)

- Delete dead OTelTestHelper.cs (zero callers, YAGNI)
- Remove 3 unused OTel packages from Unit.csproj
- Fix double-lookup in MetricCollectionHarness (TryGetValue)
- Fix unnecessary allocation in GetMeasurements (Array.Empty)
- Make Collect() atomic (lock spans clear+record)
- Replace Task.Yield() with Task.Delay(10, CancellationToken.None)
- Remove duplicate assertions in 3 test methods
- Add using to all MeteredMemoryCache instances (12 total)
- Fix typo in XML doc comment

All 226 tests pass (208 unit + 18 integration).
coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 15, 2026
- Delete unused OTelTestHelper.cs class (dead code with no callers)
- Remove unnecessary OpenTelemetry NuGet packages that supported it
- Fix MeteredMemoryCache_EvictionCallback_AfterDisposal_NoMetricUpdate test:
  - Use CancellationChangeToken to trigger TokenExpired eviction
  - TokenExpired IS counted by _evictionCount, unlike Remove
  - This properly tests the disposal guard (the original test using
    inner.Remove triggered EvictionReason.Removed which is excluded
    from the counter regardless of disposal guard)
@cursor

This comment has been minimized.

Fix vacuous assertion that would pass when no measurements exist. The
Assert.All call passes trivially on an empty collection, allowing a
broken implementation emitting zero 'cache.requests' measurements to
pass the test.

Added Assert.NotEmpty(measurements) guard before Assert.All to ensure
the test fails if no measurements are emitted, consistent with the
pattern used in CoverageGapTests.cs for AdditionalTags tests.
@cursor
Copy link

cursor bot commented Feb 15, 2026

Bugbot Autofix prepared fixes for 1 of the 1 bugs found in the latest run.

  • ✅ Fixed: Vacuous assertion passes when no measurements exist
    • Added Assert.NotEmpty(measurements) guard before Assert.All in AssertAllMeasurementsHaveTags to prevent vacuous passing on empty collections.

coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 15, 2026
- Eliminate coverage theater: replace Assert.Contains(true,...) with AssertAllMeasurementsHaveTags in 3 shared test methods
- Fix double-lookup in GetMeasurementsWithTags (TryGetValue pattern)
- Remove DRY violation: AssertMeasurementCount delegates to AssertAggregatedCount
- Add AggregateException catch in Collect() for post-disposal safety
- Improve comment accuracy for type-mismatch and eviction scenarios

226 tests pass (208 unit + 18 integration), 0 warnings.
…teredMemoryCacheTests

Boy Scout Rule: 6 instances of Assert.Contains(true, ...Select(m => m.Tags.Any(...)))
verified 'any measurement has tag' instead of 'ALL measurements have tag'.

Added AssertAllMeasurementsHaveTag to TestListener with NotEmpty guard.
Replaced all 6 instances across 4 tests:
- WithCacheName_EmitsCacheNameTag
- OptionsConstructor_WithAdditionalTags_EmitsAllTagsOnMetrics
- TryGetValue_Generic_WithNamedCache_RecordsMetricsWithCacheName
- GetOrCreate_WithNamedCache_RecordsMetricsWithCacheName

Zero Assert.Contains(true,...) patterns remain in the test suite.
@rjmurillo rjmurillo merged commit 0b532b2 into main Feb 15, 2026
7 checks passed
@rjmurillo rjmurillo deleted the test/eliminate-coverage-theater branch February 15, 2026 07:42
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is ON, but it could not run because the branch was deleted or merged before Autofix could start.

private readonly List<MetricMeasurement> _measurements = new();
private readonly Dictionary<string, List<MetricMeasurement>> _measurementsByInstrument = new();
private readonly Dictionary<string, long> _aggregatedCounters = new();
private readonly string[] _instrumentNames;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused _instrumentNames field in shared harness

Low Severity

The _instrumentNames field is assigned at construction but never read. The lambda on line 83 captures the constructor parameter instrumentNames directly instead of the field _instrumentNames. This is inconsistent with the TestListener class in MeteredMemoryCacheTests.cs, which correctly references _instrumentNames in its lambda. The behavior is identical today since both reference the same array, but the dead field is confusing.

Additional Locations (2)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments