https://github.com/cezarypiatek/approvaltestsextensions
A set of helper classes that facilitate the usage of ApprovalTests.NET library
https://github.com/cezarypiatek/approvaltestsextensions
Last synced: 8 days ago
JSON representation
A set of helper classes that facilitate the usage of ApprovalTests.NET library
- Host: GitHub
- URL: https://github.com/cezarypiatek/approvaltestsextensions
- Owner: cezarypiatek
- License: mit
- Created: 2021-05-29T11:09:16.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2025-02-27T20:30:46.000Z (8 months ago)
- Last Synced: 2025-10-04T06:31:37.501Z (14 days ago)
- Language: C#
- Size: 88.9 KB
- Stars: 6
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ApprovalTestsExtensions
A set of helper classes that facilitate the usage of [ApprovalTests.NET library](https://github.com/approvals/approvaltests.net).## How to install
`ApprovalTestsExtensions` is distribute as a nuget package [SmartAnalyzers.ApprovalTestsExtensions](https://www.nuget.org/packages/SmartAnalyzers.ApprovalTestsExtensions/)## What problems does it solve?
1. Approvals.Net doesn't work correctly inside the `async` methods called from the test method. `ApprovalTestsExtensions` solves this problem by providing a custom implementation of `IApprovalNamer` which takes the leverage of `[CallerFilePath]` and `[CallerMemberName]` attributes. Required parts for a snapshot file name are resolved during the compilation hence there is no need to analyze the stack trace anymore.
2. Approvals.Net doesn't provide a way to ignore some parts of the asserted payload. `ApprovalTestsExtensions` allows to specify ignored attributes for JSON payload with [JsonPath](https://github.com/json-path/JsonPath).
3. Approvals.Net doesn't have an out-of-the-box mechanism for bulk updates of the existing snapshots. `ApprovalTestsExtensions` provides a custom implementation `IReporterWithApprovalPower` which allows to perform such operation.
4. Calling Approvals multiple times within a single method without scenario name or with the same scenario results with snapshot overrides. This is highly undesirable and quite often caused by copy&paste mistakes. `ApprovalTestsExtensions` tracks snapshot names per instance and prevents overrides by throwing `SnapshotOverriddenException` in such a case.
5. IMHO the mechanism of defining reporters is unnecessarily complicated and sometimes might result in a time-consuming investigation of why the selected reporter is not used, especially in the build server environment or when using async lambdas. `ExplicitApprover` allows for specifying reporters explicitly during the instantiation. This library provides `BuildServerReporter` which serves as a decorator for the underlying reporter allowing to run it only in the Build Server environment. There's also `EnhancedInlineDiffReporter` which produces rich, git-like, inline diff reports - very useful for reporting differences in CI. By default, `ExplicitApprover` uses the following reporter setup:
```cs
new FirstWorkingReporter
(
new BuildServerReporter(new EnhancedInlineDiffReporter()),
new DiffReporter()
)
```> **IMPORTANT:** The `ExplicitApprover` is completely ignoring everything what is configured with `[UseReporter]` and `[FrontLoadedReporter]`. The reporter needs to be explicitly passed to the constructor, otherwise it fallbacks to the default setup presented above.
You can achieve the same result without using `ExplicitApprover` in the following way:
```cs
[assembly: FrontLoadedReporter(typeof(MyTestReporter))]public class MyTestReporter: IEnvironmentAwareReporter
{
private readonly FirstWorkingReporter _underlyingReporter;public MyTestReporter()
{
_underlyingReporter = new FirstWorkingReporter
(
new BuildServerReporter(new EnhancedInlineDiffReporter()),
new DiffReporter()
);
}public void Report(string approved, string received) => _underlyingReporter.Report(approved, received);
public bool IsWorkingInThisEnvironment(string forFile) => true;
}
```
## How to use itJust create an instance of `SmartAnalyzers.ApprovalTestsExtensions.ExplicitApprover` class and start using it for approving your snapshots. The `ExplicitApprover` class offers the following helper methods:
- `VerifyHttpResponse` - Checking the returned JSON payload with saved snapshot.
- `VerifyHttpResponseForScenario` - Same as above but allows to specify scenario name. Helpful when there is more than one snapshot inside a single test.
- `VerifyJson` - Checking the provided explicitly JSON payload with a saved snapshot.
- `VerifyJsonForScenario` - Same as above but allows specifying scenario name. Helpful when there is more than one snapshot inside a single test.
- `VerifyJsonDiff` - Calculate the diff between the provided JSON payloads and checking the result against the saved snapshot. More info about the diff can be found on [jsondiffpatch.net]( https://github.com/wbish/jsondiffpatch.net) project site. Similar method available for `HttpResponseMessage`.All those methods allow for ignoring attributes inside the payload by specifying them as `ignoredPaths` in the form of [JsonPath](https://github.com/json-path/JsonPath). This is especially useful when the payload contains dynamically generated data like dates or identifiers.
**IMPORTANT**: `ExplicitApprover` is memorizing test method name during the constructor invocation so it should be created per every test method. You should not re-use `ExplicitApprover` instance between test methods as it will result in incorrect snapshot names. If you want to extract `ExplicitApprover` instance creation to the method then all constructor parameters should be passed explicitly.
Example test can look as follows:
```cs
[Test]
public async Task should_get_details_for_newly_registered_user()
{
// STEP 1: Create a new instance of the ExplicitApprover
var approver = new ExplicitApprover();
// STEP 2: Create API client for our Service
var applicationFactory = new SampleApplicationFactory();
var apiClient = applicationFactory.CreateClient();// STEP 3: Call register user endpoint
var registerUserResponse = await apiClient.PostAsJsonAsync(new {
Login = "JohnDoe",
Email = "john@doe.com",
Password = "Secret#1234"
});// STEP 4: Verify the response snapshot
await approver.VerifyHttpResponseForScenario("RegisterUserResponse", registerUserResponse);
// STEP 5: Call the user details endpoint
var registerResult = await registerUserResponse.Content.ReadAsAsync();
var userDetailsResponse = await apiClient.GetAsync("/user/" + registerResult.Id);// STEP 6: Verify the response snapshot ignoring the id property inside the payload
await approver.VerifyHttpResponseForScenario("NewUserDetails", userDetailsResponse, new []{"$.id"});
}
```## Bulk snapshot update
To perform an update of all existing snapshots just set the static property `ExplicitApprover.UseAutoApprover` to true or specify it during the `ExplicitApprover` instance creation:
```cs
public async Task sample_test()
{
// STEP 1: Create a new instance of the ExplicitApprover with `useAutoApprover`
var approver = new ExplicitApprover(useAutoApprover: true);
}
```## Credits
This project is built upon other great open-source projects:
- https://github.com/approvals/ApprovalTests.Net
- https://github.com/mmanela/diffplex
- https://github.com/wbish/jsondiffpatch.net
- https://github.com/JamesNK/Newtonsoft.Json