{"id":22070379,"url":"https://github.com/posinformatique/posinformatique.logging.assertions","last_synced_at":"2026-02-21T08:36:38.342Z","repository":{"id":199745148,"uuid":"703435648","full_name":"PosInformatique/PosInformatique.Logging.Assertions","owner":"PosInformatique","description":"PosInformatique.Logging.Assertions is a library to mock and assert easily the logs generated by the ILogger interface.","archived":false,"fork":false,"pushed_at":"2025-02-11T08:41:06.000Z","size":102,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-11-02T22:11:13.127Z","etag":null,"topics":["assertions","c-sharp","fluent","fluent-assertions","logging","mock","mocking","tdd","xunit"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PosInformatique.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,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-10-11T08:38:48.000Z","updated_at":"2025-02-19T09:24:19.000Z","dependencies_parsed_at":null,"dependency_job_id":"20d993f9-739c-40b4-97e1-d495d9d2243f","html_url":"https://github.com/PosInformatique/PosInformatique.Logging.Assertions","commit_stats":null,"previous_names":["posinformatique/posinformatique.logging.assertions"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/PosInformatique/PosInformatique.Logging.Assertions","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PosInformatique%2FPosInformatique.Logging.Assertions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PosInformatique%2FPosInformatique.Logging.Assertions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PosInformatique%2FPosInformatique.Logging.Assertions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PosInformatique%2FPosInformatique.Logging.Assertions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PosInformatique","download_url":"https://codeload.github.com/PosInformatique/PosInformatique.Logging.Assertions/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PosInformatique%2FPosInformatique.Logging.Assertions/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29677609,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T06:23:40.028Z","status":"ssl_error","status_checked_at":"2026-02-21T06:23:39.222Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["assertions","c-sharp","fluent","fluent-assertions","logging","mock","mocking","tdd","xunit"],"created_at":"2024-11-30T20:16:12.367Z","updated_at":"2026-02-21T08:36:38.327Z","avatar_url":"https://github.com/PosInformatique.png","language":"C#","readme":"# PosInformatique.Logging.Assertions\nPosInformatique.Logging.Assertions is a library to mock and assert easily the logs generated by the ILogger interface.\n\n## Installing from NuGet\nThe [PosInformatique.Logging.Assertions](https://www.nuget.org/packages/PosInformatique.Logging.Assertions/)\nlibrary is available directly on the\n[![Nuget](https://img.shields.io/nuget/v/PosInformatique.Logging.Assertions)](https://www.nuget.org/packages/PosInformatique.Logging.Assertions/)\nofficial website.\n\nTo download and install the library to your Visual Studio unit test projects use the following NuGet command line \n\n```\nInstall-Package PosInformatique.Logging.Assertions\n```\n\n## How it is work?\n\nImagine that you have the following class which contains various log:\n\n```csharp\npublic class CustomerManager\n{\n    private readonly IEmailProvider emailProvider;\n\n    private readonly ILogger\u003cCustomerManager\u003e logger;\n\n    public CustomerManager(IEmailProvider emailProvider, ILogger\u003cCustomerManager\u003e logger)\n    {\n        this.emailProvider = emailProvider;\n        this.logger = logger;\n    }\n\n    public async Task SendEmailAsync(int id, string name)\n    {\n        this.logger.LogInformation(\"Starting to send an email to the customer '{Name}' with the identifier '{Id}'\", name, id);\n\n        using (this.logger.BeginScope(new { Id = id }))\n        {\n            try\n            {\n                this.logger.LogDebug(\"Call the SendAsync() method\");\n\n                await this.emailProvider.SendAsync(name);\n\n                this.logger.LogDebug(\"SendAsync() method has been called.\");\n\n                this.logger.LogInformation(\"Email provider has been called.\");\n            }\n            catch (Exception exception)\n            {\n                this.logger.LogError(exception, \"Unable to send the email !\");\n            }\n        }\n    }\n}\n\npublic interface IEmailProvider\n{\n    Task SendAsync(string name);\n}\n```\n\nAs a good developer (like me), who always do unit tests with 100% of code coverage, you have to write the unit tests\nto test the `SendEmailAsync()` method and mock the `IEmailProvider` and `ILogger\u003cT\u003e` interfaces with your favorite mocking library.\n([Moq](https://github.com/devlooped/moq) for me...).\n\n\u003e Some developers consider that log information should not be test with unit tests... :laughing: :laughing: \n`ILogger` interface and his methods calls should be test as any normal code and specially the scope to be sure we\ninject the right data. Most often, developpers discover that their own logs don't log things correctly in production\nenvironments... :laughing: :laughing: \n\nSo the unit test to write for the previous example should look like something like that:\n\n```csharp\n[Fact]\npublic async Task SendEmailAsync()\n{\n    // Arrange\n    var emailProvider = new Mock\u003cIEmailProvider\u003e(MockBehavior.Strict);\n    emailProvider.Setup(ep =\u003e ep.SendAsync(\"Gilles TOURREAU\"))\n        .Returns(Task.CompletedTask);\n\n    var logger = new Mock\u003cILogger\u003cCustomerManager\u003e\u003e(MockBehavior.Strict);\n    logger.Setup(l =\u003e l.Log(LogLevel.Information, \"Starting to send an email to the customer '{Name}' with the identifier '{Id}'\", ..., ..., ... )) // WTF???\n        ...\n    logger.Setup(l =\u003e l.BeginScope\u003c...\u003e(...))   // WTF???\n\n    var manager = new CustomerManager(emailProvider.Object, logger.Object);\n\n    // Act\n    await manager.SendEmailAsync(1234, \"Gilles TOURREAU\");\n\n    // Assert\n    emailProvider.VerifyAll();\n    logger.VerifyAll();\n}\n\n```\n\nAs your can see, it is very hard to mock the [Log()](https://learn.microsoft.com/fr-fr/dotnet/api/microsoft.extensions.logging.ilogger.log)\nmethod of the [ILogger](https://learn.microsoft.com/fr-fr/dotnet/api/microsoft.extensions.logging.ilogger)\ninterface which have the following signature:\n\n```csharp\nvoid Log\u003cTState\u003e(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func\u003cTState, Exception?, string\u003e formatter)\n```\n\nAnd also to check the scope usage with the [ILogger](https://learn.microsoft.com/fr-fr/dotnet/api/microsoft.extensions.logging.ilogger)\ninterface it can be hard!\n\nThe [PosInformatique.Logging.Assertions](https://www.nuget.org/packages/PosInformatique.Logging.Assertions/) library\nallows the developpers to mock the [ILogger](https://learn.microsoft.com/fr-fr/dotnet/api/microsoft.extensions.logging.ilogger)\neasily and setup the sequence of the expected logs using a fluent style code. For the previous example,\nthis is how the unit test look like for the previous example.\n\n```csharp\n[Fact]\npublic async Task SendEmailAsync()\n{\n    // Arrange\n    var emailProvider = new Mock\u003cIEmailProvider\u003e(MockBehavior.Strict);\n    emailProvider.Setup(ep =\u003e ep.SendAsync(\"Gilles TOURREAU\"))\n        .Returns(Task.CompletedTask);\n\n    var logger = new LoggerMock\u003cCustomerManager\u003e();\n    logger.SetupSequence()\n        .LogInformation(\"Starting to send an email to the customer '{Name}' with the identifier '{Id}'\")\n            .WithArguments(\"Gilles TOURREAU\", 1234)\n        .BeginScope(new { Id = 1234 })\n            .LogDebug(\"Call the SendAsync() method\")\n            .LogDebug(\"SendAsync() method has been called.\")\n            .LogInformation(\"Email provider has been called.\")\n        .EndScope();\n\n    var manager = new CustomerManager(emailProvider.Object, logger.Object);\n\n    // Act\n    await manager.SendEmailAsync(1234, \"Gilles TOURREAU\");\n\n    // Assert\n    emailProvider.VerifyAll();\n    logger.VerifyLogs();\n}\n```\n\n:heart_eyes: :heart_eyes: Sexy isn't it??? The unit test is more easy to read and write!\n\n\u003e Do not forget to call the `VerifyLogs()` at the end of your unit test like a `VerifyAll()` call with the\n[Moq](https://github.com/devlooped/moq) library. The `VerifyLogs()` will check that all methods setup (*Arrange*) are called\nby the code under test (*Act*).\n\nDo not hesitate to use the indentation to make the fluent code more readable specially when\nusing nested scopes.\n\nFor example to check nested log scopes write the following code with the following indented code:\n```csharp\nvar logger = new LoggerMock\u003cCustomerManager\u003e();\nlogger.SetupSequence()\n    .LogInformation(\"Starting to send an email to the customer '{Name}' with the identifier '{Id}'\")\n        .WithArguments(\"Gilles TOURREAU\", 1234)\n    .BeginScope(new { Id = 1234 })\n        .BeginScope(new { Name = \"Gilles\" })\n            .LogError(\"Error in the scope 1234 + Gilles\")\n        .EndScope()\n        .LogInformation(\"Log between the 2 nested scopes.\")\n        .BeginScope(new { Name = \"Aiza\" })\n            .LogError(\"Error in the scope 1234 + Aiza\")\n        .EndScope()\n    .EndScope();\n```\n\n### Test the error logs with an exception.\nTo test an `Exception` with specified in the `LogError()` method of the `ILogger` interface use the\n`WithException()` method an set the instance expected:\n\n```csharp\n[Fact]\npublic async Task SendEmailAsync_WithException()\n{\n    // Arrange\n    var theException = new FormatException(\"An exception\");\n\n    var emailProvider = new Mock\u003cIEmailProvider\u003e(MockBehavior.Strict);\n    emailProvider.Setup(ep =\u003e ep.SendAsync(\"Gilles TOURREAU\"))\n        .ThrowsAsync(theException);\n\n    var logger = new LoggerMock\u003cCustomerManager\u003e();\n    logger.SetupSequence()\n        .LogInformation(\"Starting to send an email to the customer '{Name}' with the identifier '{Id}'\")\n            .WithArguments(\"Gilles TOURREAU\", 1234)\n        .BeginScope(new { Id = 1234 })\n            .LogDebug(\"Call the SendAsync() method\")\n            .LogError(\"Unable to send the email !\")\n                .WithException(theException)\n        .EndScope();\n\n    var manager = new CustomerManager(emailProvider.Object, logger.Object);\n\n    // Act\n    await manager.Invoking(m =\u003e m.SendEmailAsync(1234, \"Gilles TOURREAU\"))\n        .Should().ThrowExactlyAsync\u003cFormatException\u003e();\n\n    // Assert\n    emailProvider.VerifyAll();\n    logger.VerifyLogs();\n}\n```\n\nIn the case the exception is throw by the code (It is mean the exception is not produced by the unit test\nduring the *Arrange* phase), use the version with a delegate to check the content of the Exception:\n\n```csharp\nvar logger = new LoggerMock\u003cCustomerManager\u003e();\nlogger.SetupSequence()\n    .LogInformation(\"Starting to send an email to the customer '{Name}' with the identifier '{Id}'\")\n        .WithArguments(\"Gilles TOURREAU\", 1234)\n    .BeginScope(new { Id = 1234 })\n        .LogDebug(\"Call the SendAsync() method\")\n        .LogError(\"Unable to send the email !\")\n            .WithException(e =\u003e\n            {\n                e.Message.Should().Be(\"An exception\");\n                e.InnerException.Should().BeNull();\n            })\n    .EndScope();\n```\n\n### Test log message templates\nThe power of this library is the ability to assert the\n[log message templates](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/#log-message-template)\nincluding the arguments. (*You know the kind of log messages\nwhich contains the vital identifiers to search in emergency in production environment and are often bad logged by the developpers...*\n:laughing: :laughing:).\n\nTo assert the log message templates parameters use the `WithArguments()` method which is available with 2 overloads:\n- `WithArguments(params object[] expectedArguments)`: Allows to specify the expected arguments of the log message template.\n- `WithArguments(int expectedCount, Action\u003cLogMessageTemplateArguments\u003e expectedArguments)`: Allows to specify\nan delegate to assert complex arguments.\n\nFor example, to assert the following log message:\n```csharp\nthis.logger.LogInformation(\"Starting to send an email to the customer '{Name}' with the identifier '{Id}'\", name, id);\n```\n\nUsing the first way with the `WithArguments(params object[] expectedArguments)` method:\n\n```csharp\nvar logger = new LoggerMock\u003cCustomerManager\u003e();\nlogger.SetupSequence()\n    .LogInformation(\"Starting to send an email to the customer '{Name}' with the identifier '{Id}'\")\n        .WithArguments(\"Gilles TOURREAU\", 1234)\n\n    ... // Continue the setup expected log sequence\n```\n\nUsing the second way with the `WithArguments(int expectedCount, Action\u003cLogMessageTemplateArguments\u003e expectedArguments)` method\nwhich give you more control of the assertions:\n\n```csharp\nvar logger = new LoggerMock\u003cCustomerManager\u003e();\nlogger.SetupSequence()\n    .LogInformation(\"Starting to send an email to the customer '{Name}' with the identifier '{Id}'\")\n        .WithArguments(2, args =\u003e\n        {\n            args[\"Name\"].Should().Be(\"Gilles TOURREAU\");\n            args[\"Id\"].Should().Be(1234);\n        })\n\n    ... // Continue the setup expected log sequence\n```\n\n\u003e Here we use the FluentAssertions library to check the arguments values of the log message template, but of course you can use your\nfavorite assertion library to check it.\n\nThe second way allows also to check the arguments of the log template message by there index (*it is not what I recommand,\nbecause if the trainee developper in your team change the name of the arguments name in the log message template, you will not\nsee the impacts in your unit tests execution...*):\n\n```csharp\nvar logger = new LoggerMock\u003cCustomerManager\u003e();\nlogger.SetupSequence()\n    .LogInformation(\"Starting to send an email to the customer '{Name}' with the identifier '{Id}'\")\n        .WithArguments(2, args =\u003e\n        {\n            args[0].Should().Be(\"Gilles TOURREAU\");\n            args[1].Should().Be(1234);\n        })\n\n    ... // Continue the setup expected log sequence\n```\n\n### Test the BeginScope() state\nIf you use the `BeginScope` method in your logging process, you can assert the content of state\nspecified in argument using two methods.\n\nFor example, to assert the following code:\n```\nusing (this.logger.BeginScope(new StateInfo() { Id = 1234 }))\n{\n    ... // Other Log\n}\n```\n\nWith the `StateInfo` class as simple like like that:\n```csharp\npublic class StateInfo\n{\n    public int Id { get; set; }\n}\n```\n\nYou can assert the `BeginScope()` method call using an anonymous object:\n\n```csharp\nvar logger = new LoggerMock\u003cCustomerManager\u003e();\nlogger.SetupSequence()\n    .BeginScope(new { Id = 1234 })\n       ... // Other Log() assertions\n    .EndScope();\n```\n\n\u003e The `BeginScope()` assertion check the equivalence (property by property and not the reference itself)\nbetween the actual object in the code and the expected object in the assertion.\n\nOr you can assert the `BeginScope()` method call using a delegate if your state object is complex:\n\n```csharp\nvar logger = new LoggerMock\u003cCustomerManager\u003e();\nlogger.SetupSequence()\n    .BeginScope\u003cState\u003e(state =\u003e\n    {\n        state.Id.Should().Be(1234);\n    })\n       ... // Other Log() assertions\n    .EndScope();\n```\n\n### Application Insights dictionary state\nIf you use Application Insights as output of your logs, the `BeginScope()` state argument must take a dictionary of string/object as the following code sample:\n\n```\nusing (this.logger.BeginScope(new Dictionary\u003cstring, object\u003e() { { \"Id\", 1234 } }))\n{\n    ... // Other Log\n}\n```\n\nTo assert the `BeginScope()` in the previous sample code, you can use the `SetupSequence().BeginScope(Object)` method assertion as pass the expected\ndictionary as argument.\n\n```csharp\nvar logger = new LoggerMock\u003cCustomerManager\u003e();\nlogger.SetupSequence()\n    .BeginScope(new Dictionary\u003cstring, object\u003e() { { \"Id\", 1234 } })\n       ... // Other Log() assertions\n    .EndScope();\n```\n\nThe [PosInformatique.Logging.Assertions](https://www.nuget.org/packages/PosInformatique.Logging.Assertions/) library provides a\n`SetupSequence().BeginScopeAsDictionary(Object)` method which allows to assert the content of the dictionary using an object (Each property and his value of the expected\nobject is considered as a key/value couple of the dictionary). Do not hesitate to use anonymous object in your unit test to make the code more easy to read.\n\nThe following example have the same behavior as the previous example, but is more easy to read by removing the dictionary instantiation and some extract brackets:\n\n```csharp\nvar logger = new LoggerMock\u003cCustomerManager\u003e();\nlogger.SetupSequence()\n    .BeginScopeAsDictionary(new { Id = 1234 })\n       ... // Other Log() assertions\n    .EndScope();\n```\n\n### Test the IsEnabled() calls\nTo test the call of the `IsEnabled(LogLevel)` call the setup method in the sequence with `LogLevel` expected. Also you have to define the value\nwhich have to be returned when method is called.\n\nFor example, imagine the following code to test:\n```csharp\npublic class CustomerManager\n{\n    private readonly ILogger\u003cCustomerManager\u003e logger;\n\n    public CustomerManager(ILogger\u003cCustomerManager\u003e logger)\n    {\n        this.logger = logger;\n    }\n\n    public string SendEmail(int id, string name)\n    {\n        if (this.logger.IsEnabled(LogLevel.Information))\n        {\n            this.logger.LogInformation(\"Starting to send an email to the customer.\");\n\n            return \"Log information enabled\";\n        }\n\n        this.logger.LogDebug(\"The log information is not enabled...\");\n\n        return \"Log information not enabled\";\n    }\n}\n```\n\nTo test the previous code just write the following unit tests:\n\n```csharp\npublic class CustomerManagerTest\n{\n    [Fact]\n    public void Test_WithInformationEnabled()\n    {\n        var logger = new LoggerMock\u003cCustomerManager\u003e();\n        logger.SetupSequence()\n            .IsEnabled(LogLevel.Information)\n                .Returns(true)\n            .LogInformation(\"Starting to send an email to the customer.\");\n\n        var manager = new CustomerManager(logger.Object);\n\n        var result = manager.SendEmail();\n\n        result.Should().Be(\"Log information enabled\");\n\n        logger.VerifyAllLogs();\n    }\n\n    [Fact]\n    public void Test_WithInformationDisabled()\n    {\n        var logger = new LoggerMock\u003cCustomerManager\u003e();\n        logger.SetupSequence()\n            .IsEnabled(LogLevel.Information)\n                .Returns(false)\n            .LogDebug(\"The log information is not enabled...\");\n\n        var manager = new CustomerManager(logger.Object);\n\n        var result = manager.SendEmail();\n\n        result.Should().Be(\"Log information not enabled\");\n\n        logger.VerifyAllLogs();\n    }\n}\n```\n\n## Assertion fail messages\nThe [PosInformatique.Logging.Assertions](https://www.nuget.org/packages/PosInformatique.Logging.Assertions/) library\ntry to make the assert fail messages the most easy to understand for the developers:\n\n![Assertion Failed Too Many Calls](https://raw.githubusercontent.com/PosInformatique/PosInformatique.Logging.Assertions/main/docs/AssertionFailedTooManyCalls.png)\n![Assertion Missing Logs](https://raw.githubusercontent.com/PosInformatique/PosInformatique.Logging.Assertions/main/docs/AssertionMissingLogs.png)\n\n## Non generic support of the ILogger interface\nThe [PosInformatique.Logging.Assertions](https://www.nuget.org/packages/PosInformatique.Logging.Assertions/) library\nallows to mock the `ILogger\u003cT\u003e` and `ILogger` interfaces.\n\nTo mock a `ILogger\u003cT\u003e` implementation use the following code:\n```csharp\nvar logger = new LoggerMock\u003cCustomerManager\u003e();\nlogger.SetupSequence()\n    .LogInformation(\"...\");\n```\n\nTo mock a `ILogger` implementation use the following code:\n```csharp\nvar logger = new LoggerMock();\nlogger.SetupSequence()\n    .LogInformation(\"...\");\n```\n\nBoth usage offers the same fluent assertion methods.\n\n## Library dependencies\n- The [PosInformatique.Logging.Assertions](https://www.nuget.org/packages/PosInformatique.Logging.Assertions/) target the .NET Standard 2.0\nand the version 2.0.0 of the [Microsoft.Extensions.Logging.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.Logging.Abstractions) NuGet package. So this library can be used with\ndifferent .NET architecture projects (.NET Framework, .NET Core, Xamarin,...) and also with old versions of the\n[Microsoft.Extensions.Logging.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.Logging.Abstractions) NuGet package.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fposinformatique%2Fposinformatique.logging.assertions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fposinformatique%2Fposinformatique.logging.assertions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fposinformatique%2Fposinformatique.logging.assertions/lists"}