[API Proposal]: FakeLogger add ability to get unmodified state
- Dominant language
- C#
- Stars
- 3.2k
- Forks
- 894
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 23
Description
### Background and motivation
We're using `Microsoft.Extensions.Logging` for structured logging and want to write unit tests for it, so we tried using `FakeLogger` but [it converts every state value to a string](https://github.com/dotnet/extensions/blob/ef51c637075afde1e28df3d78e9e955141f05328/src/Libraries/Microsoft.Extensions.Diagnostics.Testing/Logging/FakeLogger.cs#L110), which is quite annoying and makes it difficult to assert these values for more complex types, where you want to preserve the structure in the associated data. Even for `bool`, `int`, `Guid` etc you need to make sure that you assert against the string form instead of the original value.
It would be nice to be able to preserve and assert against the original unmodified state.
### API Proposal
```csharp
namespace Microsoft.Extensions.Logging.Testing;
public class FakeLogRecord
{
public object? RawState { get; init; }
}
```
### API Usage
```csharp
// NUnit example
[Test]
public void Test()
{
var logger = new FakeLogger();
var id = Guid.NewGuid();
logger.LogInformation("The id is {id}", id);
Assert.That(logger.LatestRecord.RawState, Is.EquivalentTo(new Dictionary
{
{ "{OriginalFormat}", "The id is {id}" },
{ "id", id }, // should not have to ToString() this
}));
}
```
### Alternative Designs
- add an option on the `FakeLogger` that skips the string conversion on `FakeLogRecord.State`
- only use the string conversion for `FakeLogRecord.StructuredState` and not `FakeLogRecord.State` (but this is a breaking change)
### Risks
Adding `RawState` with an init-only setter should be backwards compatible but they are not supported out of the box for all versions of .NET (e.g. standard/framework).
It could use a public setter here but then it's mutable, although that probably doesn't matter too much as it's only used by tests.
Adding a new parameter the existing constructor would be a breaking change, although most people probably aren't constructing this class from their own code. An optional parameter would be source compatible but binary breaking, but that might be acceptable given that it's only used by unit tests and unlikely to be a transitive dependency. Adding a second constructor overload would not be a breaking change.
Contributor guide
Assessment
This issue has not been assessed yet.