SweetMock.Extensions.Logging
0.9.41
dotnet add package SweetMock.Extensions.Logging --version 0.9.41
NuGet\Install-Package SweetMock.Extensions.Logging -Version 0.9.41
<PackageReference Include="SweetMock.Extensions.Logging" Version="0.9.41" />
<PackageVersion Include="SweetMock.Extensions.Logging" Version="0.9.41" />
<PackageReference Include="SweetMock.Extensions.Logging" />
paket add SweetMock.Extensions.Logging --version 0.9.41
#r "nuget: SweetMock.Extensions.Logging, 0.9.41"
#:package SweetMock.Extensions.Logging@0.9.41
#addin nuget:?package=SweetMock.Extensions.Logging&version=0.9.41
#tool nuget:?package=SweetMock.Extensions.Logging&version=0.9.41
SweetMock.Extensions.Logging
A powerful extension library for mocking ILogger<T> in .NET tests using the SweetMock framework. Capture, filter, and verify log messages with ease.
Features
- Mock ILogger<T>: Create fully controllable logger mocks for testing
- Log Level Filtering: Configure minimum log levels to capture
- Message Capture: Access both raw log arguments and formatted messages
- Flexible Verification: Filter and assert on log entries by level, event ID, state, and message content
- Seamless Integration: Works with standard Microsoft.Extensions.Logging patterns
- LoggerMessage Support: Compatible with source-generated logging methods
Installation
dotnet add package SweetMock.Extensions.Logging
Quick Start
Using with Fixtures
[Fixture<MyService>]
public class MyServiceTests
{
[Fact]
public void ServiceLogs_AreVerified()
{
// Arrange
var fixture = Fixture.MyService(); // Default log configuration is used.
var sut = fixture.CreateMyService();
// Act
sut.ProcessData();
// Assert
var errorLogs = fixture.Calls.logger.Log(t => t.logLevel == LogLevel.Error);
Assert.Single(errorLogs);
}
}
public class MyService(ILogger<MyService> logger)
{
public void ProcessData()
{
logger.LogInformation("Processing started");
logger.LogError("Processing failed");
logger.LogInformation("Processing completed");
}
}
Basic Logger Mock
using Microsoft.Extensions.Logging;
[Mock<ILogger<MyService>, MockLog<MyService>>]
public class MyServiceTests
{
[Fact]
public void LogMessages_AreCaptured()
{
// Arrange
var logger = Mock.ILogger<MyService>();
// Act
logger.LogInformation("User {UserId} logged in", 123);
logger.LogWarning("Slow operation detected");
logger.LogError("Failed to process request");
// Assert - Access captured logs
var logs = ((MockLog<MyService>)logger).GetLogs();
Assert.Equal(3, logs.Count());
}
}
Log Level Filtering
Setting Minimum Log Level
By default, MockLog<T> captures logs at LogLevel.Information and above. You can configure this:
[Fact]
public void CaptureAllLogs_IncludingTrace()
{
// Arrange
var fixture = Fixture.MyService(config =>
config.logger.SetLogLevel(LogLevel.Trace)
);
var sut = fixture.CreateMyService();
// Act
sut.DoWork();
// Assert - Now Trace and Debug logs are captured
Assert.NotEmpty(fixture.Calls.logger.Log(t => t.logLevel == LogLevel.Trace));
Assert.NotEmpty(fixture.Calls.logger.Log(t => t.logLevel == LogLevel.Debug));
}
Verifying Log Entries
Filter by Log Level
var informationLogs = fixture.Calls.logger.Log(t => t.logLevel == LogLevel.Information);
var warningsAndErrors = fixture.Calls.logger.Log(t =>
t.logLevel == LogLevel.Warning || t.logLevel == LogLevel.Error
);
Filter by Event ID
var specificEvent = fixture.Calls.logger.Log(t => t.eventId == 1001);
Access Formatted Messages
Use LogWithMessage() to access the formatted log message text:
[Fact]
public void LogMessage_ContainsExpectedText()
{
// Arrange
var fixture = Fixture.MyService();
var sut = fixture.CreateMyService();
// Act
sut.ProcessUser(userId: 42);
// Assert
var logEntry = Assert.Single(fixture.Calls.logger.LogWithMessage(
args => args.eventId == 5
));
Assert.Equal(LogLevel.Information, logEntry.logLevel);
Assert.Contains("User 42", logEntry.message);
}
Access Log State
The state property contains structured logging data:
[Fact]
public void LogState_ContainsStructuredData()
{
// Arrange
var fixture = Fixture.MyService();
var sut = fixture.CreateMyService();
// Act
logger.LogInformation("Processing {Operation} for {UserId}", "Update", 123);
// Assert
var log = Assert.Single(fixture.Calls.logger.Log());
if (log.state is IReadOnlyList<KeyValuePair<string, object?>> state)
{
Assert.Contains(state, kvp => kvp.Key == "Operation" && kvp.Value?.ToString() == "Update");
Assert.Contains(state, kvp => kvp.Key == "UserId" && kvp.Value?.ToString() == "123");
}
}
LoggerMessage Source Generator Support
MockLog<T> works seamlessly with the high-performance LoggerMessage attribute:
public partial class MyService(ILogger<MyService> logger)
{
public void ProcessOrder(string orderId)
{
LogOrderProcessing(logger, orderId);
}
[LoggerMessage(EventId = 100, Level = LogLevel.Information, Message = "Processing order {OrderId}")]
static partial void LogOrderProcessing(ILogger<MyService> logger, string orderId);
}
[Fixture<MyService>]
[Mock<ILogger<MyService>, MockLog<MyService>>]
public class MyServiceTests
{
[Fact]
public void LoggerMessage_IsCaptured()
{
// Arrange
var fixture = Fixture.MyService();
var sut = fixture.CreateMyService();
// Act
sut.ProcessOrder("ORD-123");
// Assert
var log = Assert.Single(fixture.Calls.logger.LogWithMessage(
args => args.eventId == 100
));
Assert.Equal("Processing order ORD-123", log.message);
}
}
Complete Example
[Fixture<UserService>]
[Mock<ILogger<UserService>, MockLog<UserService>>]
public class UserServiceTests(ITestOutputHelper output)
{
[Fact]
public void UserLogin_LogsCorrectly()
{
// Arrange
var fixture = Fixture.UserService(config =>
config.logger.SetLogLevel(LogLevel.Debug)
);
var sut = fixture.CreateUserService();
// Act
sut.LoginUser("john.doe@example.com");
// Assert
var logs = fixture.Calls.logger.LogWithMessage();
// Output all logs to test output
foreach (var log in logs)
{
output.WriteLine(log.ToString());
}
// Verify specific log entries
Assert.Single(logs, log =>
log.logLevel == LogLevel.Information &&
log.message!.Contains("Login attempt")
);
Assert.Single(logs, log =>
log.logLevel == LogLevel.Information &&
log.message!.Contains("Login successful")
);
// Ensure no errors were logged
Assert.Empty(fixture.Calls.logger.Log(t =>
t.logLevel == LogLevel.Error || t.logLevel == LogLevel.Critical
));
}
}
public class UserService(ILogger<UserService> logger)
{
public void LoginUser(string email)
{
logger.LogDebug("Validating user {Email}", email);
logger.LogInformation("Login attempt for {Email}", email);
// Simulate login logic
logger.LogInformation("Login successful for {Email}", email);
}
}
API Reference
MockLog<TCategoryName>
The main mock logger class that extends MockOf_ILogger<TCategoryName>.
Constructor
public MockLog(Action<MockConfig>? config, MockOptions? options)
Configuration
// Set minimum log level
config.logger.SetLogLevel(LogLevel.Debug)
// Default behaviors are pre-configured:
// - BeginScope returns a disposable
// - IsEnabled returns true for Information and above
Log Entry Properties
Both Log_Arguments and LogWithMessage_Arguments contain:
InstanceName: The mock instance nameMethodSignature: The logging method signaturelogLevel: The log level (Trace, Debug, Information, Warning, Error, Critical)eventId: The event ID associated with the logstate: The state object containing structured log dataexception: The exception, if any
LogWithMessage_Arguments additionally contains:
message: The formatted log message string
Logs Collection
// Access raw log arguments
fixture.Calls.logger.Log()
fixture.Calls.logger.Log(filter: args => args.logLevel == LogLevel.Error)
// Access logs with formatted messages
fixture.Calls.logger.LogWithMessage()
fixture.Calls.logger.LogWithMessage(filter: args => args.message.Contains("error"))
// Filter by other properties
fixture.Calls.logger.Log(args => args.eventId == 100)
fixture.Calls.logger.Log(args => args.exception != null)
Common Patterns
Verify No Errors
Assert.Empty(fixture.Calls.logger.Log(t =>
t.logLevel == LogLevel.Error || t.logLevel == LogLevel.Critical
));
Verify Log Sequence
var logs = fixture.Calls.logger.Log().ToList();
Assert.Equal(LogLevel.Information, logs[0].logLevel);
Assert.Equal(LogLevel.Warning, logs[1].logLevel);
Assert.Equal(LogLevel.Error, logs[2].logLevel);
Count Logs by Level
var errorCount = fixture.Calls.logger.Log(t => t.logLevel == LogLevel.Error).Count();
Assert.Equal(2, errorCount);
Verify Message Content
var messageLog = Assert.Single(fixture.Calls.logger.LogWithMessage(
args => args.message!.Contains("Expected text")
));
Requirements
- .NET 8.0, 9.0, or 10.0
- SweetMock
- Microsoft.Extensions.Logging.Abstractions
License
See the main SweetMock repository for license information.
Links
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
- SweetMock (>= 0.9.41)
-
net8.0
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
- SweetMock (>= 0.9.41)
-
net9.0
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
- SweetMock (>= 0.9.41)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.