.NET / SQL / Enterprise Engineering
Enterprise Test Strategy And Refactoring Roadmap
Report summary
For a large ASP.NET MVC docket and property-lead platform that is preserving county-specific behavior while expanding to flexible nationwide support, the safest direction is a modular application architecture with explicit boundaries: controllers at the presentation edge, application-level orchestra
Key topics
- .NET / SQL / Enterprise Engineering
- .NET
- SQL
- Enterprise Engineering
- AI
- TypeScript
- Runtime
- Semantic Systems
- Research Archive
Research provenance
For citation, use the report title and canonical URL. Archival presence does not establish authorship or promote report statements into portfolio evidence.
Source availability: 38 citation markers in the source export have no recoverable source links. Those markers are omitted from this reader; any supplied bibliography and ordinary links remain. Check the original sources before relying on the cited claims.
This page renders the archived Markdown as safe, formatted HTML. It is background research and does not become a portfolio claim without evidence review.
Full report
On this page
Recommended operating model
For a large ASP.NET MVC docket and property-lead platform that is preserving county-specific behavior while expanding to flexible nationwide support, the safest direction is a modular application architecture with explicit boundaries: controllers at the presentation edge, application-level orchestration in a service or planner layer, domain rules in policy objects, and infrastructure concerns behind repositories and external adapters. Microsoft’s architecture guidance explicitly ties maintainability and testability to dependency inversion, ports-and-adapters style boundaries, and keeping persistence and infrastructure concerns outside the core model; ASP.NET MVC guidance also emphasizes constructor-injected explicit dependencies for controllers.
That architecture lines up well with how Microsoft recommends testing MVC applications: controller unit tests should focus on controller behavior, not on routing, filters, or model binding; broader request handling belongs in integration tests. At the same time, EF Core guidance is clear that tests using the real production-style database are materially different from unit tests, and the in-memory provider is discouraged for realistic data behavior. For an enterprise rollout, that means most decision logic should move out of controllers into highly testable planner and policy classes, while repository and transaction behavior should be validated with real database integration tests.
The practical implication is that this system should stop treating “county behavior” as controller behavior and start treating it as versioned application rules plus adapter contracts. That shift is what makes it possible to protect Pinellas and Hillsborough, introduce new county profiles safely, and keep regression coverage stable as controllers are split up. This is an inference from the architecture and testing guidance above, but it is the natural way to make nationwide flexibility compatible with strong regression control.
Refactoring roadmap
The first move should not be “rewrite the big controllers.” It should be “freeze observable behavior.” Martin Fowler’s refactoring guidance stresses preserving observable behavior through small changes, and snapshot or approval testing tools such as Verify are designed precisely for locking down complex outputs before refactoring. For this system, that means creating characterization baselines for the current Pinellas and Hillsborough flows: admin catalog results, launch-readiness decisions, source-template readiness outcomes, repair audit records, normalized reports, and extension-capture results.
After that baseline exists, split each large controller action into five responsibilities. The controller should only perform HTTP concerns: auth, antiforgery, model-state handling, calling one planner or service, and returning the correct result. The planner should orchestrate a use case end-to-end: selecting county profile, computing work steps, choosing fallbacks, and coordinating dependent services. Policies should hold pure business decisions such as launch readiness, source-template readiness, county support rules, durable report identity rules, and repair eligibility. Services should execute side-effecting application work such as bulk worklist execution, report creation, repair audit persistence, and evidence persistence. Adapters should isolate infrastructure and external systems: repositories, county-site clients, browser-extension bridges, AI clients, template executors, file stores, and identity or clock providers. This decomposition is consistent with Microsoft’s application-layer guidance and ports-and-adapters architecture guidance.
A useful implementation sequence is to extract one vertical slice at a time instead of one layer everywhere at once. Start with the highest-risk workflows: admin launch readiness, source-template readiness, bulk worklists, and AI-assisted repair. For each slice, introduce a request DTO or command, a single planner interface, policy objects for decisions, and adapters for every live dependency. The planner becomes the place where county variability is composed, and the controller becomes almost trivial. Microsoft’s DI guidance and testing guidance both support this pattern because explicit dependencies sharply improve unit testability.
For county rollout, model county support as versioned configuration plus policy, not as if/else branches spread across controllers, views, and repositories. Keep county manifests separate from the orchestration code, and version the county profile with the source-template contract it supports. That recommendation is an inference, but it directly follows from the separation-of-concerns guidance and from the need to localize change for nationwide expansion. It also makes characterization testing much easier, because each county profile can become a reusable regression fixture.
Test pyramid and seam placement
A strong pyramid for this system should be intentionally bottom-heavy, but not dogmatic. Fowler’s practical test-pyramid guidance and Microsoft’s testing guidance both point toward many focused unit tests, fewer integration tests, and the smallest number of end-to-end tests necessary to cover cross-layer confidence. For this docket platform, a good recommended starting distribution is roughly two-thirds planner, policy, and domain-level unit tests; about one-quarter integration tests across database and adapters; and a thin top slice of Playwright-driven UI and extension regression tests for the highest-value workflows. That ratio is a recommendation based on the cited guidance, not a published rule.
The line between controller tests and planner or service tests should be strict. Controller tests should verify only: authorization and bad model handling, route-to-command mapping, correct result types and redirects, and whether the controller passes the right request into the planner. Planner and service tests should own the actual business expectations: county selection rules, catalog filtering rules, launch readiness, source-template readiness, bulk worklist planning, repair gating, property evidence normalization, and durable report identity rules. That is exactly in line with Microsoft’s guidance that controller unit tests avoid routing, filters, and model binding, while component interaction is handled in integration tests.
Integration tests should cover every place where two or more components must collaborate correctly to preserve behavior: repositories against the real database engine, template execution plus persistence, planner-to-repository transactions, audit writes, report persistence, and controller endpoints where auth, antiforgery, and serialization matter. Microsoft’s ASP.NET integration-test guidance specifically distinguishes this broader scope from controller unit testing, and EF Core guidance recommends testing against the production database system when realistic behavior matters. For a large county-variant workflow engine, that realism matters.
For admin catalog filtering, launch readiness, source-template readiness, repair audit, and bulk worklists, the most effective split is: policy tests for decision matrices, planner tests for orchestration and dependency interaction, integration tests for data retrieval and persistence, and a small number of UI or endpoint tests to ensure the admin workflow still behaves correctly through the real request pipeline. Use lower-level endpoint integration tests where possible, and reserve browser tests for workflows where operator interaction or rendered HTML behavior is business-critical. Microsoft’s integration-test guidance explicitly calls out lower-level endpoint testing via Application Parts when the goal is endpoint correctness rather than full browser rendering.
AI-assisted repair should never be tested through live model calls in normal CI. The stable pattern is to put the model behind a transport-level port such as an IRepairModelClient, test prompt assembly and policy decisions separately, and use deterministic fake or mocked responses for the AI adapter. Microsoft’s Kiota guidance explicitly recommends mocking the transport layer for API-client tests rather than calling real APIs, and .NET guidance notes that handlers can be registered separately for mocking. That same pattern cleanly applies to an LLM or AI repair adapter.
Browser-extension fallback should likewise be tested without live county sites. Playwright officially supports Chrome extension testing, request routing, HAR replay, and test fixtures, while Chrome’s own guidance calls out the need to test extension service-worker suspension and restart behavior. For this platform, that means building local, replayable county-page fixtures: static HTML pages for DOM parsing behaviors, HAR-based replay for site traffic, and service-worker restart scenarios to ensure extension fallback survives background-worker churn.
For property evidence and durable report identity, the safest testing seam is a pure value-object and policy seam. Microsoft’s DDD guidance emphasizes immutability for value objects, and Azure reliability guidance emphasizes idempotency when the same logical work may be processed more than once. A durable report identity should therefore be computed from canonical, immutable report inputs rather than from incidental runtime state; tests should prove that equivalent evidence inputs yield the same identity and materially changed evidence yields a different identity. This design recommendation is an inference from the cited value-object and idempotency guidance.
Recommended test fixtures and builders
Use shared fixtures aggressively, but only at the right levels. xUnit’s official shared-context guidance supports class, collection, and assembly fixtures for expensive or reusable setup, and its parallelism guidance makes it practical to isolate mutable resources or disable parallel execution for collections that share state. For this system, that suggests a small fixture catalog rather than ad hoc helper sprawl: a database fixture for real SQL integration tests, a web-host fixture for endpoint tests, a Playwright extension fixture for browser-extension scenarios, and a deterministic system fixture that provides clock, ID, filesystem, and AI stubs.
For database-backed integration tests, prefer the real database engine in throwaway environments. EF Core’s testing guidance is explicit that testing with the real database is important, and the in-memory provider is discouraged for realistic testing; Testcontainers exists specifically to boot real dependencies in tests. If this system runs SQL Server in production, the integration suite should run SQL Server in CI or a comparable production-like engine rather than relying on fake providers for repository behavior.
For object setup, use a layered builder strategy rather than giant object mother classes. AutoFixture’s official documentation centers on reducing arrange noise and making object graph creation easier, which is useful, but enterprise systems still benefit from explicit semantic builders for important business scenarios. The best combination here is AutoFixture for routine object generation plus hand-written builders for business-significant fixtures such as CountyProfileBuilder, TemplateReadinessBuilder, PropertyEvidenceBuilder, RepairAttemptBuilder, BulkWorklistBuilder, and DurableReportBuilder. That keeps tests readable and makes Pinellas, Hillsborough, and future county variants first-class fixtures instead of buried JSON blobs.
Snapshot fixtures should be part of the core strategy, not a side technique. Verify is designed for complex data models and documents, and Playwright supports visual comparisons and snapshot assertions for browser output. In this system, snapshot coverage is especially valuable for normalized reports, rendered evidence sections, source-template outputs, repair audits, county capability manifests, and selected admin UI states. Use textual or JSON snapshots for business artifacts, and use Playwright screenshots only for a narrow set of operator-visible screens where layout regressions matter.
Recorded dependency fixtures are also essential. For county-site behaviors, keep one replay pack per county profile using HAR or WireMock-style recorded interactions. Playwright supports replay from HAR at the browser-context level, while WireMock supports record-and-playback with request verification. That lets the suite exercise extension fallback and county parsing behavior without depending on external sites, rate limits, uptime, or markup drift on every PR.
Critical regression suites
The single most important suite is the county baseline suite. For Pinellas and Hillsborough, lock down the behaviors that matter to the business, not just internal method outputs: source selection, template readiness, fallback selection, evidence extraction, normalized report output, durable report identity, repair audit results, and visible admin readiness states. Snapshot testing is particularly well suited here because the outputs are complex and because the near-term goal is behavior preservation during refactoring.
The next suite should be the admin workflow regression suite. This suite should cover catalog filtering, launch-readiness calculations, source-template readiness, repair-audit browsing, and bulk worklist execution through the real HTTP pipeline, including authorization and antiforgery where relevant. Microsoft’s integration-test guidance explicitly addresses endpoint-level testing, including scenarios that need antiforgery-aware handling, and this is where that guidance pays off. Controller unit tests alone will not protect these flows.
A separate AI repair regression suite should validate deterministic repair behavior without live AI. It should include prompt-building snapshots, parser and schema-validation tests, retry and fallback policy tests, negative-case tests for malformed or partial model output, and audit persistence tests showing exactly what repair was proposed, accepted, rejected, or retried. The live AI adapter should have only a very small contract suite, using mocked transport or recorded responses, because transport-level stubbing is the officially recommended way to test API clients without unstable external dependencies.
The browser-extension fallback suite should be treated as a first-class regression suite, not as a fringe UI suite. Playwright officially supports extension loading and fixtures, and Chrome’s extension guidance highlights service-worker lifecycle behavior as a correctness concern. That means the suite should prove at least these cases: normal capture on a local county fixture page, fallback capture when the page structure deviates, service-worker restart without losing durable state, operator handoff when automated extraction fails, and offline or replay-mode capture via HAR or local stubs.
The durable evidence and report suite should prove identity stability and idempotency. Equivalent evidence should produce the same normalized representation and the same report identity; semantically meaningful data changes should produce a different identity; and replayed or duplicated processing should not create inconsistent audit or persistence records. Those are exactly the kinds of guarantees that immutable value-object design and idempotent processing patterns are intended to provide.
Finally, keep a small top-of-pyramid operator suite in Playwright for the few workflows where browser truth matters: one admin smoke flow, one bulk worklist flow, one county capture flow, one repair-review flow, and one report-acceptance flow. Playwright’s own guidance favors resilient locators, isolated contexts, page objects, fixtures, and trace capture, which is the right way to keep this top layer useful without letting it become brittle.
CI gate proposal and risk-based prioritization
A credible enterprise CI design for this system should use required fast PR gates plus slower full regression gates. On every pull request, the required backend gate should run formatting, analyzers, controller and planner unit tests, policy tests, and a selected integration slice. Microsoft’s dotnet format supports separate whitespace, style, and analyzer passes driven by .editorconfig, and .NET’s code-analysis tooling is designed to be configured centrally through EditorConfig or AnalyzerConfig files. That gives you a clean way to enforce whitespace and style without code-review churn.
The frontend PR gate should run extension or UI unit tests plus a small Playwright smoke package. Playwright’s CI guidance, fixtures, isolation model, and trace viewer are well suited to making these runs diagnosable when they fail, which is critical for browser-extension work. Keep the PR Playwright package intentionally small and deterministic; save broader visual comparisons and full extension replay suites for a slower gate.
For security and supply chain, use more than one gate because they catch different problems. GitHub’s dependency review action is designed specifically to block pull requests that introduce vulnerable dependencies. Dependabot alerts and the dependency graph provide ongoing visibility into vulnerable packages. CodeQL provides code scanning for compiled languages like C# and for JavaScript or TypeScript, and GitHub also supports scanning workflow files. Secret scanning push protection is designed to stop secrets before they are pushed. In practice, that means: dependency review should be a required PR check, secret scanning push protection should be enabled at the repository or organization level, and CodeQL should run on the default branch and on a schedule, with PR execution where performance is acceptable.
For package and vulnerability auditing beyond GitHub-native controls, npm audit should run against the frontend dependency tree because it exits successfully only when no vulnerabilities are found unless configured otherwise, and OWASP Dependency-Check remains a recognized SCA tool for known-vulnerability detection. I would treat npm audit as a PR or daily gate depending on signal quality, but I would place OWASP Dependency-Check in a scheduled or nightly gate rather than on every PR unless false-positive management is already mature. The existence and purpose of both tools are documented; the “nightly not PR-required” recommendation is a risk-management inference intended to keep merge friction proportional.
Coverage should be measured, but not used as the sole quality proxy. .NET provides cross-platform code-coverage collection through dotnet-coverage and built-in test collection options. The right use here is thresholding the planner, policy, and adapter seams that are supposed to carry business logic, while relying on snapshot and integration suites to cover behavior that raw line coverage does not capture well.
The hardening work that should be prioritized first is shown below.
| Priority | Work item | Why it should go first |
|---|---|---|
| Highest | Freeze Pinellas and Hillsborough with characterization and snapshot baselines | Refactoring without behavior baselines is the fastest way to break legacy county logic; Fowler-style safe refactoring depends on preserving observable behavior. |
| High | Extract AI, county-site, extension, clock, and report-ID dependencies behind ports | These are the seams that let unit and integration tests become deterministic and remove live external calls from CI. |
| High | Split the largest controllers into controller plus planner plus policy plus adapter boundaries | Microsoft’s MVC testing guidance strongly favors thin controller behavior tests and broader logic below the controller. |
| High | Replace fake DB testing with real-database integration coverage | EF guidance discourages the in-memory provider for realistic behavior and recommends testing against the production database system when correctness matters. |
| Medium | Build offline county replay packs for extension fallback and parser drift | HAR replay, routing, and service-worker lifecycle testing remove dependence on live county sites while still exercising realistic browser behavior. |
| Medium | Establish durable report identity and evidence canonicalization as immutable, policy-tested logic | Immutability and idempotency are what protect reused or replayed work from creating inconsistent results. |
| Medium | Add required PR security gates and scheduled deep scans | Dependency review, CodeQL, secret scanning, and package auditing cover different failure classes and are strongest when layered together. |
The overall recommendation is to treat this effort as behavior-preserving modularization, not as a generic cleanup project. Freeze the legacy counties first, move decisions into planner and policy seams, validate data and persistence with real integration tests, simulate AI and county-site behavior deterministically, and let CI enforce a narrow set of fast required checks plus broader scheduled regression and security scans. That combination is the most credible path to nationwide rollout without sacrificing the working county-specific behavior that the business already depends on.