{"id":13621447,"url":"https://github.com/alefranz/MELT","last_synced_at":"2025-04-15T01:32:40.536Z","repository":{"id":52514527,"uuid":"216249000","full_name":"alefranz/MELT","owner":"alefranz","description":"MELT is a free, open source, testing library for the .NET Standard Microsoft Extensions Logging library. It is a solution to easily test logs.","archived":false,"fork":false,"pushed_at":"2023-08-06T21:36:02.000Z","size":648,"stargazers_count":69,"open_issues_count":8,"forks_count":6,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-05-29T11:05:22.332Z","etag":null,"topics":["dotnet","logging","microsoft-extensions-logging","testing"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alefranz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-10-19T18:02:15.000Z","updated_at":"2024-04-22T08:35:27.000Z","dependencies_parsed_at":"2024-01-14T05:00:03.612Z","dependency_job_id":"e7cc2a32-367a-41d1-904e-fe2cbce38aa6","html_url":"https://github.com/alefranz/MELT","commit_stats":{"total_commits":33,"total_committers":2,"mean_commits":16.5,"dds":"0.030303030303030276","last_synced_commit":"8f689e58319825e3401a19812db8b82cff2bcc89"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alefranz%2FMELT","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alefranz%2FMELT/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alefranz%2FMELT/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alefranz%2FMELT/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alefranz","download_url":"https://codeload.github.com/alefranz/MELT/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248989362,"owners_count":21194577,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dotnet","logging","microsoft-extensions-logging","testing"],"created_at":"2024-08-01T21:01:06.447Z","updated_at":"2025-04-15T01:32:40.136Z","avatar_url":"https://github.com/alefranz.png","language":"C#","funding_links":[],"categories":["C#"],"sub_categories":[],"readme":"# MELT\n\n\u003c!-- markdownlint-disable no-inline-html --\u003e\n\u003cimg align=\"right\" width=\"128\" height=\"128\" src=\"logo.png\"\u003e\n\u003c!-- markdownlint-enable no-inline-html --\u003e\n\n_Testing Library for Microsoft Extensions Logging._\n\n[![Build Status](https://github.com/alefranz/MELT/workflows/Build/badge.svg?branch=main)](https://github.com/alefranz/MELT/actions?query=branch%3Amain)\n[![MELT Nuget](https://img.shields.io/nuget/v/MELT?label=MELT\u0026logo=nuget)](https://www.nuget.org/packages/MELT/)\n[![MELT.AspNetCore Nuget](https://img.shields.io/nuget/v/MELT.AspNetCore?label=MELT.AspNetCore\u0026logo=nuget)](https://www.nuget.org/packages/MELT.AspNetCore/)\n[![MELT.Serilog Nuget](https://img.shields.io/nuget/v/MELT.Serilog?label=MELT.Serilog\u0026logo=nuget)](https://www.nuget.org/packages/MELT.Serilog/)\n[![MELT.Serilog.AspNetCore Nuget](https://img.shields.io/nuget/v/MELT.Serilog.AspNetCore?label=MELT.Serilog.AspNetCore\u0026logo=nuget)](https://www.nuget.org/packages/MELT.Serilog.AspNetCore/)\n[![MELT.Xunit Nuget](https://img.shields.io/nuget/v/MELT.Xunit?label=MELT.Xunit\u0026logo=nuget)](https://www.nuget.org/packages/MELT.Xunit/)\n\n\u003c!-- omit in toc --\u003e\n## About MELT\n\nMELT is a free, open-source, testing library for the .NET Standard _Microsoft Extensions Logging_ library.\nIt is a solution to easily test logs.\n\nIt is a repackaging with a sweetened API and some omissions of [Microsoft.Extensions.Logging.Testing](https://github.com/aspnet/Extensions/tree/master/src/Logging/Logging.Testing), a library used internally in [ASP.NET Core](https://github.com/aspnet/AspNetCore) for testing the logging, given that [there is currently no plan to offer an official package for it](https://github.com/aspnet/Extensions/issues/672#issuecomment-532850535).\n\nIt is licensed under [Apache License 2.0](https://github.com/alefranz/MELT/blob/main/LICENSE).\nMost of the code is copyrighted by the .NET Foundation as mentioned in the files headers.\n\nIf you like this project please don't forget to *star* it on [GitHub](https//github.com/alefranz/MELT) or let me know with a [tweet](https://twitter.com/AleFranz).\n\nYou can find an explanation on the advantages of using this library and the importance of testing logs on the blog post \"[How to test logging when using Microsoft.Extensions.Logging](https://alessio.franceschelli.me/posts/dotnet/how-to-test-logging-when-using-microsoft-extensions-logging/)\".\n\n\u003e If you are upgrading to version 0.5, you can _optionally_ migrate to the new syntax. See [Upgrade from 0.4 and below](#upgrade-from-04-and-below) for more info.\n\n\u003c!-- omit in toc --\u003e\n## Index\n\n- [Quickstart](#quickstart)\n- [Assertions](#assertions)\n  - [Assert log entries](#assert-log-entries)\n  - [Assert scopes](#assert-scopes)\n  - [Assert log original format](#assert-log-original-format)\n  - [Assert exceptions in log entries](#assert-exceptions-in-log-entries)\n  - [Easily test log or scope properties with xUnit](#easily-test-log-or-scope-properties-with-xunit)\n  - [And much more](#and-much-more)\n  - [Full example](#full-example)\n- [Quickstart for ASP.NET Core integration tests](#quickstart-for-aspnet-core-integration-tests)\n  - [Assert log entries and scopes](#assert-log-entries-and-scopes)\n  - [Full example](#full-example-1)\n- [Compatibility](#compatibility)\n- [Serilog compatibility using Serilog.Extensions.Logging](#serilog-compatibility-using-serilogextensionslogging)\n  - [Full example](#full-example-2)\n- [Serilog compatibility using Serilog.AspNetCore](#serilog-compatibility-using-serilogaspnetcore)\n  - [Assert log entries](#assert-log-entries-1)\n  - [Assert scopes on an entry](#assert-scopes-on-an-entry)\n  - [Assert message format](#assert-message-format)\n  - [Easily test log or scope properties with xUnit](#easily-test-log-or-scope-properties-with-xunit-1)\n  - [And much more](#and-much-more-1)\n  - [Full example](#full-example-3)\n- [NLog compatibility using NLog.Web.AspNetCore](#nlog-compatibility-using-nlogwebaspnetcore)\n  - [Full example](#full-example-4)\n- [Upgrade from 0.6](#upgrade-from-06)\n- [Upgrade from 0.4 and below](#upgrade-from-04-and-below)\n  - [Upgrade of ASP.NET Core Integration Tests](#upgrade-of-aspnet-core-integration-tests)\n\n## Quickstart\n\n- Install the NuGet package [MELT](https://www.nuget.org/packages/MELT/)\n\n    ```xml\n    \u003cPackageReference Include=\"MELT\" Version=\"0.9.0\" /\u003e\n    ```\n\n    \u003e Note: due to a breaking change in `Microsoft.Extensions.Logging` 3.1, if you are testing a project that references **only** `Microsoft.Extensions.Logging.Abstractions` 3.1+, you need to add a reference to `Microsoft.Extensions.Logging` 3.1+ in your test project:\n    \u003e\n    \u003e ```xml\n    \u003e \u003cPackageReference Include=\"Microsoft.Extensions.Logging\" Version=\"3.1.0\" /\u003e\n    \u003e ```\n\n- Get a test logger factory\n\n    ```csharp\n    var loggerFactory = TestLoggerFactory.Create();\n    ```\n\n- Get a logger from the factory, as usual, to pass to your fixture.\n\n    ```csharp\n    var logger = loggerFactory.CreateLogger\u003cSample\u003e();\n    ```\n\n## Assertions\n\n### Assert log entries\n\nThe logger factory exposes a property `Sink` to access the sink that collects the logs. The sink exposes a property `LogEntries` that enumerates all the captured logs.\nEach entry exposes all the relevant property for a log.\n\nFor example, to test with xUnit that a single log has been emitted and it has a specific message:\n\n```csharp\nvar log = Assert.Single(loggerFactory.Sink.LogEntries);\nAssert.Equal(\"The answer is 42\", log.Message);\n```\n\n### Assert scopes\n\nEach log entry exposes a property `Scopes` to have the scopes active for the particular logger captured with that entry.\n_Note that the scopes of a log entry are local to a specific logger and async context._\n\n```csharp\nvar log = Assert.Single(loggerFactory.Sink.LogEntries);\nvar log = Assert.Single(log.Scopes);\nAssert.Equal(\"This scope's answer is 42\", scope.Message);\n```\n\nIt is also possible to get all the scopes generated across all loggers. The logger factory exposes a property `Sink` to access the sink that collects the logs. The sink exposes a property `Scopes` that enumerates all the captured scopes.\n\nFor example, to test with xUnit that a single scope has been emitted and it had a specific message:\n\n```csharp\nvar scope = Assert.Single(loggerFactory.Sink.Scopes);\nAssert.Equal(\"I'm in the GET scope\", scope.Message);\n```\n\n### Assert log original format\n\nThe original format used to generate the log entry, before the message is rendered, is captured in the property `OriginalMessage`.\n\n```csharp\nvar log = Assert.Single(loggerFactory.Sink.LogEntries);\nAssert.Equal(\"The answer is {number}\", log.OriginalFormat);\n```\n\n### Assert exceptions in log entries\n\nThe log entry exposes a property `Exception` which contains the exception captured by the logger.\n\nFor example, to test with xUnit that a single log has been emitted with a specific exception and assert a exception property:\n\n```csharp\nvar log = Assert.Single(loggerFactory.Sink.LogEntries);\nvar exception = Assert.IsType\u003cArgumentNullException\u003e(log.Exception);\nAssert.Equal(\"foo\", exception.ParamName);\n```\n\n### Easily test log or scope properties with xUnit\n\n- Install the NuGet package [MELT.Xunit](https://www.nuget.org/packages/MELT.Xunit/)\n\n    ```xml\n    \u003cPackageReference Include=\"MELT.Xunit\" Version=\"0.9.0\" /\u003e\n    ```\n\n- Use the `LoggingAssert.Contains(...)` helpers.\nFor example, to test that a single log has been emitted and it had a property `number` with value `42`:\n\n    ```csharp\n    var log = Assert.Single(loggerFactory.Sink.LogEntries);\n    LoggingAssert.Contains(\"number\", 42, log.Properties);\n    ```\n\n### And much more\n\nYou can assert againt all the characteristic of a log entry: `EventId`, `Exception`, `LoggerName`, `LogLevel`, `Message`, `OriginalFormat`, `Properties` and `Scope`.\n\n### Full example\n\nSee [Samples](https://github.com/alefranz/MELT/tree/main/samples)\n\n## Quickstart for ASP.NET Core integration tests\n\n- Install the NuGet package [MELT.AspNetCore](https://www.nuget.org/packages/MELT.AspNetCore/)\n\n    ```xml\n    \u003cPackageReference Include=\"MELT.AspNetCore\" Version=\"0.9.0\" /\u003e\n    ```\n\n- Use the `UseTestLogging(...)` extension method to add a test logger to the test web host builder, where you can also customize the behaviour.\n\n    For example to filter all log entries and scopes not generated by loggers consumed in the `SampleWebApplication.*` namespace (this filters the logger name so it assumes you are using `ILogger\u003cT\u003e` or following the default naming convention for your loggers.)\n\n    This can be done where you are already configuring the web host builder. Configure the logger using `WithWebHostBuilder` on the factory.\n\n    ```csharp\n    using Microsoft.AspNetCore.Hosting;\n    using Microsoft.AspNetCore.Mvc.Testing;\n    // ...\n    _factory = factory.WithWebHostBuilder(builder =\u003e builder\n        .UseTestLogging(options =\u003e options.FilterByNamespace(nameof(SampleWebApplication))));\n    ```\n\n    You can also filter by logger name using `FilterByTypeName\u003cT\u003e()` or `FilterByLoggerName(string name)`.\n\n    Or, if you prefer, you can use the `AddTest(...)` extension method in the `ConfigureLogging(...)` section.\n\n    ```csharp\n    using Microsoft.AspNetCore.Hosting;\n    using Microsoft.AspNetCore.Mvc.Testing;\n    using Microsoft.Extensions.Logging;\n    // ...\n    _factory = factory.WithWebHostBuilder(builder =\u003e builder\n        .ConfigureLogging(logging =\u003e logging.AddTest(options =\u003e options.FilterByNamespace(nameof(SampleWebApplication)))));\n    ```\n\n- Alternatively, you can configure the logger builder in the `ConfigureWebHost` implementation in your custom `WebApplicationFactory\u003cTStartup\u003e`.\n\n    ```csharp\n    using Microsoft.AspNetCore.Hosting;\n    using Microsoft.AspNetCore.Mvc.Testing;\n\n    namespace SampleWebApplication.IntegrationTests\n    {\n        public class CustomWebApplicationFactory\u003cTStartup\u003e : WebApplicationFactory\u003cTStartup\u003e\n            where TStartup : class\n        {\n            protected override void ConfigureWebHost(IWebHostBuilder builder)\n            {\n                builder.UseTestLogging(options =\u003e options.FilterByNamespace(nameof(SampleWebApplication)));\n            }\n        }\n    }\n    ```\n\n    You can then retrieve the sink to assert against using the extension method `GetTestLoggerSink()` on the factory.\n\n    Please note that in this case, all tests sharing the same factory will get the same sink.\n    You can reset it between tests with `Clear()` in the constructor of your `xUnit` tests. For example:\n\n    ```csharp\n    public class LoggingTestWithInjectedFactory : IClassFixture\u003cCustomWebApplicationFactory\u003cStartup\u003e\u003e\n    {\n        private readonly CustomWebApplicationFactory\u003cStartup\u003e _factory;\n\n        public LoggingTestWithInjectedFactory(CustomWebApplicationFactory\u003cStartup\u003e factory)\n        {\n            _factory = factory;\n            // In this case, the factory will be reused for all tests, so the sink will be shared as well.\n            // We can clear the sink before each test execution, as xUnit will not run this tests in parallel.\n            _factory.GetTestLoggerSink().Clear();\n            // When running on 2.x, the server is not initialized until it is explicitly started or the first client is created.\n            // So we need to use:\n            // if (_factory.TryGetTestLoggerSink(out var testLoggerSink)) testLoggerSink.Clear();\n        }\n    }\n    ```\n\n    The logger will be automatically injected with Dependency Injection.\n\n### Assert log entries and scopes\n\nOnce you access the `sink` with `_factory.GetTestLoggerSink()` you get access to the property `LogEntries` that enumerates all the captured logs and `Scopes` which enumerated all the captured `Scopes`.\nYou will then be able to do all the assertions like described above in [Assertions](#assertions)\n\nFor example, to test with xUnit that a single log has been emitted and it had a specific message:\n\n```csharp\nvar log = Assert.Single(_factory.GetTestLoggerSink().LogEntries);\nAssert.Equal(\"The answer is 42\", log.Message);\n```\n\n### Full example\n\nSee [LoggingTest](samples/current/SampleWebApplication.IntegrationTests/LoggingTest.cs) or\n[LoggingTestWithInjectedFactory](samples/current/SampleWebApplication.IntegrationTests/LoggingTestWithInjectedFactory.cs).\n\n## Compatibility\n\nThis library is compatibe with [Microsoft.Extensions.Logging](https://github.com/aspnet/Extensions/tree/master/src/Logging/Logging.Testing) 2.0+.\nWhen used for integration tests of ASP.NET Core applications, it supports all the currently supported versions of ASP.NET Core: 2.1 LTS, 3.1 LTS and 5.0, but also the now deprecated 2.2 and 3.0.\n\n## Serilog compatibility using Serilog.Extensions.Logging\n\nIf you are using [Serilog.Extensions.Logging](https://github.com/serilog/serilog-extensions-logging) the integration is straightforward as this library is fully compliant with `Microsoft.Extensions.Logging`.\n\nSimply follow the main instruction as the fact that you are plugging Serilog as the provider does not alter the behaviour.\n\n### Full example\n\nSee [LoggingTest](samples/current/serilog/SampleWebApplicationSerilog.IntegrationTests/LoggingTest.cs) or\n[LoggingTestWithInjectedFactory](samples/current/serilog/SampleWebApplicationSerilog.IntegrationTests/LoggingTestWithInjectedFactory.cs).\n\n## Serilog compatibility using Serilog.AspNetCore\n\nUnfortunately, [Serilog.AspNetCore](https://github.com/serilog/serilog-aspnetcore) doesn't plug nicely into `Microsoft.Extensions.Logging` as it replaces the logger factory and brings in an opinionated behaviour.\n\nHowever, `MELT` has specific support to allow to write tests against the Serilog produced logs, also allowing you to verify the Serilog behaviours (e.g. object expansion).\n\n- Modify your `Program.cs` of your ASP.NET Core applications, defining a `LoggerProviderCollection` to be able to hook into the logging from the tests later on. Then, pass it to the `UseSerilog()` extension method of the web host builder.\n\n    ```csharp\n    public class Program\n    {\n        public static readonly LoggerProviderCollection Providers = new LoggerProviderCollection();  // \u003c---\n\n        public static int Main(string[] args)\n        {\n            // ...\n        }\n\n        public static IHostBuilder CreateHostBuilder(string[] args)\n        {\n            Host.CreateDefaultBuilder(args)\n                .ConfigureWebHostDefaults(webBuilder =\u003e\n                {\n                    webBuilder\n                        // ...\n                        .UseSerilog(providers: Providers);  // \u003c---\n                });\n        }\n    }\n    ```\n\n- Now go back to your integration tests project, and install the NuGet package [MELT.Serilog.AspNetCore](https://www.nuget.org/packages/MELT.Serilog.AspNetCore/)\n\n    ```xml\n    \u003cPackageReference Include=\"MELT.Serilog.AspNetCore\" Version=\"0.9.0\" /\u003e\n    ```\n\n- Define a Serilog logger, setting it up to write to the providers' collection we had previously added to `Program.cs`\n\n    ```csharp\n    Log.Logger = new LoggerConfiguration()\n        .MinimumLevel.Verbose()\n        .Enrich.FromLogContext()\n        .WriteTo.Providers(Program.Providers)  // \u003c---\n        .CreateLogger();\n    ```\n\n- Use the `UseSerilogTestLogging(...)` extension method to add a test logger to the test web host builder, where you can also customize the behaviour.\n\n    For example to filter all log entries and scopes not generated by loggers consumed in the `SampleWebApplication.*` namespace (this filters the logger name so it assumes you are using `ILogger\u003cT\u003e` or following the default naming convention for your loggers.)\n\n    This can be done where you are already configuring the web host builder. Configure the logger using `WithWebHostBuilder` on the factory.\n\n    ```csharp\n    using Microsoft.AspNetCore.Hosting;\n    using Microsoft.AspNetCore.Mvc.Testing;\n    // ...\n    _factory = factory.WithWebHostBuilder(builder =\u003e builder\n        .UseSerilogTestLogging(options =\u003e options.FilterByNamespace(nameof(SampleWebApplicationSerilogAlternate))));\n    ```\n\n    You can also filter by logger name using `FilterByTypeName\u003cT\u003e()` or `FilterByLoggerName(string name)`.\n\n    Or, if you prefer, you can use the `AddSerilogTest(...)` extension method in the `ConfigureLogging(...)` section.\n\n    ```csharp\n    using Microsoft.AspNetCore.Hosting;\n    using Microsoft.AspNetCore.Mvc.Testing;\n    using Microsoft.Extensions.Logging;\n    // ...\n    _factory = factory.WithWebHostBuilder(builder =\u003e builder\n        .ConfigureLogging(logging =\u003e logging.AddSerilogTest(options =\u003e options.FilterByNamespace(nameof(SampleWebApplicationSerilogAlternate)))));\n    ```\n\n- Alternatively, you can configure the logger builder in the `ConfigureWebHost` implementation in your custom `WebApplicationFactory\u003cTStartup\u003e`.\n\n    ```csharp\n    using Microsoft.AspNetCore.Hosting;\n    using Microsoft.AspNetCore.Mvc.Testing;\n    using Serilog;\n\n    namespace SampleWebApplicationSerilogAlternate.IntegrationTests\n    {\n        public class CustomWebApplicationFactory\u003cTStartup\u003e : WebApplicationFactory\u003cTStartup\u003e\n            where TStartup : class\n        {\n            public CustomWebApplicationFactory()\n            {\n                Log.Logger = new LoggerConfiguration()\n                    .MinimumLevel.Verbose()\n                    .Enrich.FromLogContext()\n                    .WriteTo.Providers(Program.Providers)\n                    .CreateLogger();\n            }\n\n            protected override void ConfigureWebHost(IWebHostBuilder builder)\n            {\n                builder.UseSerilogTestLogging(options =\u003e\n                {\n                    options.FilterByNamespace(nameof(SampleWebApplicationSerilogAlternate));\n                });\n            }\n\n            protected override void Dispose(bool disposing)\n            {\n                if (true)\n                {\n                    Log.CloseAndFlush();\n                }\n            }\n        }\n    }\n    ```\n\n    You can then retrieve the sink to assert against using the extension method `GetSerilogTestLoggerSink()` on the factory.\n\n    Please note that in this case, all tests sharing the same factory will get the same sink.\n    You can reset it between tests with `Clear()` in the constructor of your `xUnit` tests. For example:\n\n    ```csharp\n    public class LoggingTestWithInjectedFactory : IClassFixture\u003cCustomWebApplicationFactory\u003cStartup\u003e\u003e\n    {\n        private readonly CustomWebApplicationFactory\u003cStartup\u003e _factory;\n\n        public LoggingTestWithInjectedFactory(CustomWebApplicationFactory\u003cStartup\u003e factory)\n        {\n            _factory = factory;\n            // In this case, the factory will be reused for all tests, so the sink will be shared as well.\n            // We can clear the sink before each test execution, as xUnit will not run this tests in parallel.\n            _factory.GetSerilogTestLoggerSink().Clear();\n            // When running on 2.x, the server is not initialized until it is explicitly started or the first client is created.\n            // So we need to use:\n            // if (_factory.TryGetSerilogTestLoggerSink(out var testLoggerSink)) testLoggerSink.Clear();\n        }\n    }\n    ```\n\n    The logger will be automatically injected with Dependency Injection.\n\n### Assert log entries\n\nThe sink exposes a property `LogEntries` that enumerates all the logs captured.\nEach entry exposes all the relevant property for a log.\n\nFor example, to test with xUnit that a single log has been emitted and it had a specific message:\n\n```csharp\nvar log = Assert.Single(_factory.GetSerilogTestLoggerSink().LogEntries);\nAssert.Equal(\"Hello \\\"World\\\"!\", log.Message);\n```\n\nPlease note that Serilog adds double quotes around parameters.\n\n### Assert scopes on an entry\n\nThe log entry exposes a property `Scopes` that enumerates all the scopes captured for that log entry.\n\nFor example, to test with xUnit that a single scope has been emitted and it had a specific message:\n\n```csharp\nvar log = Assert.Single(_factory.GetSerilogTestLoggerSink().LogEntries);\nvar scope = Assert.Single(log.Scope);\nAssert.Equal(new ScalarValue(\"I'm in the GET scope\"), scope);\n```\n\nThe scope is preserved in the Serilog format, so you can use the Serilog `DictionaryValue`, `ScalarValue`, `SequenceValue` or `StructureValue`.\n\nIf you have multiple nested scopes, you can assert with:\n\n```csharp\nAssert.Collection(log.Scope,\n    x =\u003e Assert.Equal(new ScalarValue(\"A top level scope\"), x),\n    x =\u003e Assert.Equal(new ScalarValue(\"I'm in the GET scope\"), x)\n);\n```\n\n### Assert message format\n\n```csharp\nvar log = Assert.Single(loggerFactory.LogEntries);\nAssert.Equal(\"The answer is {number}\", log.OriginalFormat);\n```\n\n### Easily test log or scope properties with xUnit\n\n- Install the NuGet package [MELT.Xunit](https://www.nuget.org/packages/MELT.Xunit/)\n\n    ```xml\n    \u003cPackageReference Include=\"MELT.Xunit\" Version=\"0.9.0\" /\u003e\n    ```\n\n- Use the `LoggingAssert.Contains(...)` helpers.\n    For example, to test that a single log has been emitted and it had a property `number` with value `42`:\n\n    ```csharp\n    var log = Assert.Single(_factory.GetSerilogTestLoggerSink().LogEntries);\n    LoggingAssert.Contains(\"place\", \"World\", log.Properties);\n    ```\n\n    Note that if you have added to the scope a dictionary, Serilog will only add the properties to the log entry it self, without create a scope:\n\n    ```csharp\n    Assert.Empty(log.Scope);\n    LoggingAssert.Contains(\"foo\", \"bar\", log.Properties);\n    LoggingAssert.Contains(\"answer\", 42, log.Properties);\n    ```\n\n### And much more\n\nYou can assert againt all the characteristic of a log entry: `EventId`, `Exception`, `LoggerName`, `LogLevel`, `Message`, `Properties`, `OriginalFormat` and `Scope`.\n\n### Full example\n\nSee [LoggingTest](samples/current/serilog/SampleWebApplicationSerilogAlternate.IntegrationTests/LoggingTest.cs) or\n[LoggingTestWithInjectedFactory](samples/current/serilog/SampleWebApplicationSerilogAlternate.IntegrationTests/LoggingTestWithInjectedFactory.cs).\n\n## NLog compatibility using NLog.Web.AspNetCore\n\nIf you are using [NLog.Web.AspNetCore](https://github.com/NLog/NLog.Web) the integration is straightforward as this library is fully compliant with `Microsoft.Extensions.Logging`.\n\nSimply follow the main instruction as the fact that you are plugging NLog as the provider does not alter the behaviour.\n\n### Full example\n\nSee [LoggingTest](samples/current/NLog/SampleWebApplicationNLog.IntegrationTests/LoggingTest.cs) or\n[LoggingTestWithInjectedFactory](samples/current/NLog/SampleWebApplicationNLog.IntegrationTests/LoggingTestWithInjectedFactory.cs).\n\n## Upgrade from 0.6\n\nIf you follow the deprecation warnings on `LogEntry.Scope`, you will be able to easily migrate to the new `LogEntry.Scopes`, which contains all the scopes active for the specific log entry, which means the scopes that were active for the particular logger and async context when the log entry had been generated.\n\nAlso notes that the `LogEntry.Scope` now returns the inner scope for the speicifc logger and async context, instead of the previously unexpected behaviour of returning the latest scope created on the specific logger, even if not active for that entry.\n\nAsserting scope.\n\n```csharp\nAssert.Equal(\"I'm in the GET scope\", log.Scope.Message);\n// become\nvar scope = Assert.Single(log.Scopes);\nAssert.Equal(\"I'm in the GET scope\", scope.Message);\n```\n\n## Upgrade from 0.4 and below\n\nThe library is still backward compatible, however, if you follow the deprecation warnings, you will be able to easily migrate to the new simplified syntax.\n\n\u003e Note: due to a breaking change in `Microsoft.Extensions.Logging` 3.1, if you are testing a project that references **only** `Microsoft.Extensions.Logging.Abstractions` 3.1+, you need to add a reference to `Microsoft.Extensions.Logging` 3.1+ in your test project:\n\u003e\n\u003e ```xml\n\u003e \u003cPackageReference Include=\"Microsoft.Extensions.Logging\" Version=\"3.1.0\" /\u003e\n\u003e ```\n\nHere are some common examples:\n\nSetting up logger factory for tests.\n\n```csharp\nvar loggerFactory = MELTBuilder.CreateLoggerFactory();\n// become\nvar loggerFactory = TestLoggerFactory.Create();\n```\n\nSetting up logger factory for tests, filtering messages by component.\n\n```csharp\nvar loggerFactory = MELTBuilder.CreateLoggerFactory(options =\u003e options.FilterByTypeName\u003cMyClass\u003e());\n// become\nvar loggerFactory = TestLoggerFactory.Create(options =\u003e options.FilterByTypeName\u003cMyClass\u003e());\n```\n\nAccessing captured logs from the factory\n\n```csharp\nvar log = Assert.Single(loggerFactory.LogEntries);\n// become\nvar log = Assert.Single(loggerFactory.Sink.LogEntries);\n```\n\nAccessing the original format of a log entry\n\n```csharp\nAssert.Equal(\"More is less.\", log.Format);\n// become\nAssert.Equal(\"More is less.\", log.OriginalFormat);\n```\n\nAssert against log properties\n\n```csharp\nLogValuesAssert.Contains(\"number\", 42, log);\n// become\nLoggingAssert.Contains(\"number\", 42, log.Properties);\n```\n\nAssert against scope properties\n\n```csharp\nLogValuesAssert.Contains(\"number\", 42, scope);\n// become\nLoggingAssert.Contains(\"number\", 42, scope.Properties);\n```\n\nAnd to assert log properties, the `using` is no longer needed\n\n```csharp\nusing MELT.Xunit;\n// no longer needed :)\n```\n\n### Upgrade of ASP.NET Core Integration Tests\n\nSetting up the web application factory with the test logger\n\n```csharp\n_sink = MELTBuilder.CreateTestSink(options =\u003e options.FilterByNamespace(nameof(SampleWebApplication)));\n_factory = factory.WithWebHostBuilder(builder =\u003e builder.ConfigureLogging(logging =\u003e logging.AddTestLogger(_sink)));\n// become\n_factory = factory.WithWebHostBuilder(builder =\u003e builder.UseTestLogging(options =\u003e options.FilterByNamespace(nameof(SampleWebApplication))));\n```\n\nAccessing the captured logs\n\n```csharp\nvar log = Assert.Single(_sink.LogEntries);\n// become\nvar log = Assert.Single(_factory.GetTestLoggerSink().LogEntries);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falefranz%2FMELT","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falefranz%2FMELT","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falefranz%2FMELT/lists"}