|
| 1 | +### Mockly.Http — Developer Guidelines |
| 2 | + |
| 3 | +#### Scope |
| 4 | +This document captures project-specific knowledge needed to build, test, and evolve Mockly.Http efficiently. It assumes familiarity with .NET, xUnit, and FluentAssertions. |
| 5 | + |
| 6 | +--- |
| 7 | + |
| 8 | +### Build and Configuration |
| 9 | + |
| 10 | +- Toolchain |
| 11 | + - .NET SDK: The repo uses `global.json`; current CI/runtime targets include .NET 8 and older TFMs. Local SDK 8.x is sufficient; older TFMs are built via multi-targeting. |
| 12 | + - C# language version is enforced via `Directory.Build.props` (`LangVersion` 11). Treat warnings as errors is enabled; analyzers run only for `net8.0` to keep builds fast. |
| 13 | + |
| 14 | +- Solution layout (major parts) |
| 15 | + - `Mockly.Http` — main library (multi-targeted; consumed by tests and FA integration). |
| 16 | + - `FluentAssertions.Mockly.Http.v8` — FluentAssertions extensions for this library. |
| 17 | + - `Mockly.Http.Specs` — xUnit specification-style tests targeting `net6.0` and `net472`. |
| 18 | + - `Mockly.Http.ApiVerificationTests` — Public API approval tests using `PublicApiGenerator` + `Verify`. |
| 19 | + |
| 20 | +- Fast local build |
| 21 | + - Use the Nuke build entrypoint for a consistent environment: |
| 22 | + - PowerShell: `./build.ps1` (auto-installs matching SDK if needed) |
| 23 | + - Bash: `./build.sh` |
| 24 | + - Or build directly with dotnet from the solution root: |
| 25 | + - `dotnet build Mockly.Http.sln -c Debug` |
| 26 | + |
| 27 | +- CI-specific behaviors you may notice locally |
| 28 | + - API verification tests load the compiled `Mockly.Http.dll` for each target framework discovered in `Mockly.Http.csproj` and compare it against the baselined approved API files in `Mockly.Http.ApiVerificationTests/ApprovedApi`. When those tests fail, you need to run the contents of AcceptApiChanges.ps1 or AcceptApiChanges.sh to update the approved files. |
| 29 | + - Analyzer intensity is highest for `net8.0` builds; non-`net8.0` targets suppress analyzers to optimize time. |
| 30 | + |
| 31 | +--- |
| 32 | + |
| 33 | +### Testing |
| 34 | + |
| 35 | +- Test projects and frameworks |
| 36 | + - Specs project: `Mockly.Http.Specs` targets `net6.0` and `net472`. |
| 37 | + - Packages: `xunit` 2.5, `xunit.runner.visualstudio` 3.0, `FluentAssertions` 8.8, `FluentAssertions.Web.v8`, `coverlet.collector` for coverage. |
| 38 | + - API approval: `Mockly.Http.ApiVerificationTests` uses `PublicApiGenerator` and `VerifyXunit`. It reads the main library’s target frameworks dynamically. |
| 39 | + |
| 40 | +- Run all tests |
| 41 | + - From the solution root: |
| 42 | + - `dotnet test -c Debug` |
| 43 | + - Using Rider/ReSharper, run the solution or individual projects as usual. Both `net6.0` and `net472` test TFMs are executed. |
| 44 | + |
| 45 | +- Filter tests |
| 46 | + - By fully-qualified name: |
| 47 | + - `dotnet test --filter FullyQualifiedName=Mockly.Http.Specs.MocklyHttpSpecs+BasicUsage.Can_mock_delete_request` |
| 48 | + - By class: |
| 49 | + - `dotnet test --filter FullyQualifiedName~Mockly.Http.Specs.MocklyHttpSpecs+AdvancedMatching` |
| 50 | + - By trait (if you add `[Trait("Category","…")]`): |
| 51 | + - `dotnet test --filter TestCategory=Slow` |
| 52 | + |
| 53 | +- Adding new tests |
| 54 | + - Place new spec classes under `Mockly.Http.Specs` and use xUnit attributes (`[Fact]`, `[Theory]`). Follow the nested-class structure used in `MocklyHttpSpecs` to group scenarios, e.g. `class WhenUsingAssertions`. |
| 55 | + - Prefer FluentAssertions for assertions. For HTTP-specific assertions, use the FA integration in `FluentAssertions.Mockly.Http.v8`. |
| 56 | + - If the test depends on matching HTTP requests: |
| 57 | + - Use `RequestMock` and the fluent builders (e.g., `mock.ForGet().ForPath("/api/*").RespondsWithJsonContent(...)`). |
| 58 | + - Consider `RequestCollection` to capture and assert on requests observed during the test. |
| 59 | + |
| 60 | +- Running API verification tests |
| 61 | + - These tests live in `Mockly.Http.ApiVerificationTests` and ensure the public API of `Mockly.Http` doesn’t change unexpectedly. |
| 62 | + - To re-baseline intentionally changed APIs, run the test once, inspect the diff, and then update approved files in `Mockly.Http.ApiVerificationTests/ApprovedApi`. Use PR review to confirm intended changes. |
| 63 | + |
| 64 | +- Coverage (optional) |
| 65 | + - `coverlet.collector` is already referenced. You can gather coverage via: |
| 66 | + - `dotnet test -c Debug /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura` |
| 67 | + |
| 68 | +--- |
| 69 | + |
| 70 | +### Verified demo: adding and running a test |
| 71 | + |
| 72 | +The following minimal test can be temporarily added to `Mockly.Http.Specs` to validate your environment and demonstrate filtering. We verified this flow locally and removed the file afterward to keep the repo clean. |
| 73 | + |
| 74 | +1) Create `Mockly.Http.Specs/DemoEnvironmentSpecs.cs`: |
| 75 | +``` |
| 76 | +using Xunit; |
| 77 | +
|
| 78 | +namespace Mockly.Http.Specs; |
| 79 | +
|
| 80 | +public class DemoEnvironmentSpecs |
| 81 | +{ |
| 82 | + [Fact] |
| 83 | + public void Addition_works() |
| 84 | + => Assert.Equal(2, 1 + 1); |
| 85 | +} |
| 86 | +``` |
| 87 | + |
| 88 | +2) Run only this test by FQN: |
| 89 | +``` |
| 90 | +dotnet test --filter FullyQualifiedName=Mockly.Http.Specs.DemoEnvironmentSpecs.Addition_works |
| 91 | +``` |
| 92 | + |
| 93 | +3) Run the full solution tests: |
| 94 | +``` |
| 95 | +dotnet test |
| 96 | +``` |
| 97 | + |
| 98 | +4) Delete `DemoEnvironmentSpecs.cs` once done. |
| 99 | + |
| 100 | +--- |
| 101 | + |
| 102 | +### Development Notes & Conventions |
| 103 | + |
| 104 | +- Code style and analyzers |
| 105 | + - The repo enforces warnings-as-errors and uses multiple analyzer packages for `net8.0`. Keep code clean to pass builds; fix or suppress with clear justification. |
| 106 | + - Prefer expression-bodied members for small helpers when consistent with surrounding code. Match existing naming and layout patterns. |
| 107 | + |
| 108 | +- Public surface discipline |
| 109 | + - Any change to public types/members in `Mockly.Http` should be intentional and accompanied by an API approval update. Expect PR review to focus on public API impact and diagnostics quality. |
| 110 | + |
| 111 | +- Testing guidance |
| 112 | + - Organize specs by nested classes representing feature areas, mirroring the patterns in `MocklyHttpSpecs.cs`. |
| 113 | + - Assertions: Use FluentAssertions 8. For HTTP responses, leverage `FluentAssertions.Web.v8` and `FluentAssertions.Mockly.Http.v8` extensions. |
| 114 | + - Unexpected requests: The default is fail-fast (`FailOnUnexpectedCalls = true`). When exploring scenarios that intentionally produce unexpected calls, set it to `false` locally in the test and assert on captured requests instead. |
| 115 | + |
| 116 | +- Useful scripts |
| 117 | + - `build.ps1` / `build.sh` — standardized build via Nuke bootstrapper; also used in CI. |
| 118 | + - `AcceptApiChanges.ps1` — helper when aligning approved API after intentional changes (see script contents/usage in repo). |
| 119 | + |
| 120 | +- Multi-targeting concerns |
| 121 | + - Where behavior differs across TFMs (e.g., regex or HTTP APIs), prefer tests that run on both `net6.0` and `net472` to catch divergence. Guard code with `#if` when absolutely necessary and keep such differences minimal and well-commented. |
| 122 | + |
| 123 | +--- |
| 124 | + |
| 125 | +### Troubleshooting |
| 126 | + |
| 127 | +- “API approval test fails, file not found for framework” |
| 128 | + - Ensure you built the solution before running tests so `Mockly.Http.dll` exists for each target. The approval test dynamically discovers target frameworks from `Mockly.Http.csproj` and loads the compiled DLL: build first, or just run `dotnet test` which builds for you. |
| 129 | + |
| 130 | +- “Analyzer warnings break the build” |
| 131 | + - Focus on `net8.0` target first, as analyzers are active there. Fix issues or add justified suppressions scoped as narrowly as possible. |
| 132 | + |
| 133 | +- “xUnit cannot discover tests for net472 locally” |
| 134 | + - Make sure you have a compatible .NET Framework targeting pack installed if running from legacy environments. Running through `dotnet test` generally handles both TFMs on modern SDKs. |
0 commit comments