Skip to content

Commit f18cad3

Browse files
Remove dependency to FluentAssertions.
1 parent b0e237f commit f18cad3

10 files changed

+228
-67
lines changed

README.md

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ logger.SetupSequence()
270270
```
271271

272272
> Here we use the FluentAssertions library to check the arguments values of the log message template, but of course you can use your
273-
favorite assertion framework to check it.
273+
favorite assertion library to check it.
274274

275275
The second way allows also to check the arguments of the log template message by there index (*it is not what I recommand,
276276
because if the trainee developper in your team change the name of the arguments name in the log message template, you will not
@@ -476,13 +476,3 @@ Both usage offers the same fluent assertion methods.
476476
and 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
477477
different .NET architecture projects (.NET Framework, .NET Core, Xamarin,...) and also with old versions of the
478478
[Microsoft.Extensions.Logging.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.Logging.Abstractions) NuGet package.
479-
480-
- The [PosInformatique.Logging.Assertions](https://www.nuget.org/packages/PosInformatique.Logging.Assertions/) library
481-
depends of the [FluentAssertions](https://github.com/fluentassertions/fluentassertions) library
482-
for internal assertions which is more pretty to read in the exceptions message. It is use the version 6.0.0 and of course it is compatible
483-
with the earlier version of it.
484-
485-
- Also, the [PosInformatique.Logging.Assertions](https://www.nuget.org/packages/PosInformatique.Logging.Assertions/) library
486-
use the internal [FluentAssertions](https://github.com/fluentassertions/fluentassertions) unit test
487-
provider engine detection to throw an exception (when an assertion is false) depending of the engine used to execute
488-
the unit test. For example, `XunitException` if the unit test engine used is `Xunit`.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//-----------------------------------------------------------------------
2+
// <copyright file="AssertionHelper.cs" company="P.O.S Informatique">
3+
// Copyright (c) P.O.S Informatique. All rights reserved.
4+
// </copyright>
5+
//-----------------------------------------------------------------------
6+
7+
namespace PosInformatique.Logging.Assertions
8+
{
9+
internal static class AssertionHelper
10+
{
11+
public static string ToString(object? @object)
12+
{
13+
if (@object is null)
14+
{
15+
return "null";
16+
}
17+
18+
return @object.ToString();
19+
}
20+
21+
public static void BeEquivalentTo(object actual, object expected)
22+
{
23+
if (!Equals(actual, expected))
24+
{
25+
throw new LoggingAssertionFailedException($"Expected value: {ToString(expected)}{Environment.NewLine}Actual value: {ToString(actual)}");
26+
}
27+
}
28+
29+
public static void BeEquivalentTo(IDictionary<string, object> actual, IDictionary<string, object> expected)
30+
{
31+
if (actual.Count != expected.Count)
32+
{
33+
if (expected.Count > actual.Count)
34+
{
35+
var missingKeys = new List<string>();
36+
37+
foreach (var expectedKey in expected.Keys)
38+
{
39+
if (!actual.ContainsKey(expectedKey))
40+
{
41+
missingKeys.Add(expectedKey);
42+
}
43+
}
44+
45+
var missingKeysList = string.Join(", ", missingKeys.Select(k => "\"" + k + "\""));
46+
47+
throw new LoggingAssertionFailedException($"Expected state to be a dictionary with {expected.Count} item(s), but it misses key(s) {{{missingKeysList}}}");
48+
}
49+
else
50+
{
51+
var additionalKeys = new List<string>();
52+
53+
foreach (var actualKey in actual.Keys)
54+
{
55+
if (!expected.ContainsKey(actualKey))
56+
{
57+
additionalKeys.Add(actualKey);
58+
}
59+
}
60+
61+
var additionalKeysList = string.Join(", ", additionalKeys.Select(k => "\"" + k + "\""));
62+
63+
throw new LoggingAssertionFailedException($"Expected state to be a dictionary with {expected.Count} item(s), but has additional key(s) {{{additionalKeysList}}}");
64+
}
65+
}
66+
}
67+
68+
public static void BeSameAs(Exception actual, Exception expected)
69+
{
70+
if (actual.GetType() != expected.GetType())
71+
{
72+
throw new LoggingAssertionFailedException($"Expected exception to refer to {expected.GetType().FullName} with message \"{expected.Message}\", but found {actual.GetType().FullName} with message \"{actual.Message}\".");
73+
}
74+
75+
if (!actual.Message.Equals(expected.Message, StringComparison.InvariantCulture))
76+
{
77+
throw new LoggingAssertionFailedException($"Expected exception to refer to {expected.GetType().FullName} with message \"{expected.Message}\", but found {actual.GetType().FullName} with message \"{actual.Message}\".");
78+
}
79+
}
80+
}
81+
}

src/Logging.Assertions/ILoggerMockSetupSequenceError.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
namespace PosInformatique.Logging.Assertions
88
{
9-
using FluentAssertions;
109
using Microsoft.Extensions.Logging;
1110

1211
/// <summary>

src/Logging.Assertions/LoggerMock.cs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
namespace PosInformatique.Logging.Assertions
88
{
99
using System.Globalization;
10-
using FluentAssertions;
11-
using FluentAssertions.Common;
1210
using Microsoft.Extensions.Logging;
1311

1412
/// <summary>
@@ -64,7 +62,7 @@ public void VerifyLogs()
6462
var missingCalls = this.expectedLogActions.Skip(this.expectedLogActionsIndex).Select(m => "- " + m.ExceptionDisplayMessage);
6563
var missingCallsString = string.Join(Environment.NewLine, missingCalls);
6664

67-
Services.ThrowException($"Logger has been called few times (Expected: {this.expectedLogActions.Count} calls, Actual: {this.expectedLogActionsIndex} calls).{Environment.NewLine}{missingCallsString}");
65+
throw new LoggingAssertionFailedException($"Logger has been called few times (Expected: {this.expectedLogActions.Count} calls, Actual: {this.expectedLogActionsIndex} calls).{Environment.NewLine}{missingCallsString}");
6866
}
6967
}
7068

@@ -177,14 +175,14 @@ private TLogAction GetCurrentExpectedLogAction<TLogAction>(string methodCall)
177175
{
178176
if (this.mock.expectedLogActionsIndex >= this.mock.expectedLogActions.Count)
179177
{
180-
Services.ThrowException($"The ILogger has been called too many times (Expected: {this.mock.expectedLogActions.Count} calls)");
178+
throw new LoggingAssertionFailedException($"The ILogger has been called too many times (Expected: {this.mock.expectedLogActions.Count} calls)");
181179
}
182180

183181
var expectedLogAction = this.mock.expectedLogActions[this.mock.expectedLogActionsIndex];
184182

185183
if (expectedLogAction is not TLogAction expectedLogActionTyped)
186184
{
187-
Services.ThrowException($"The '{methodCall}' method has been called but expected other action (Expected: {expectedLogAction.Name})");
185+
throw new LoggingAssertionFailedException($"The '{methodCall}' method has been called but expected other action (Expected: {expectedLogAction.Name})");
188186
throw new NotImplementedException("Must not be called.");
189187
}
190188

@@ -248,7 +246,7 @@ public void Assert<TState>(LogLevel logLevel, TState state, Exception? exception
248246
{
249247
if (logLevel != this.logLevel)
250248
{
251-
Services.ThrowException($"Wrong log level for the Log() method call. (Expected: {this.logLevel}, Actual: {logLevel})");
249+
throw new LoggingAssertionFailedException($"Wrong log level for the Log() method call. (Expected: {this.logLevel}, Actual: {logLevel})");
252250
}
253251

254252
if (this.arguments != null)
@@ -259,12 +257,12 @@ public void Assert<TState>(LogLevel logLevel, TState state, Exception? exception
259257

260258
if (originalMessage != this.message)
261259
{
262-
Services.ThrowException($"Wrong log message for the Log({logLevel}) method call. (Expected: '{this.message}', Actual: '{originalMessage}')");
260+
throw new LoggingAssertionFailedException($"Wrong log message for the Log({logLevel}) method call. (Expected: '{this.message}', Actual: '{originalMessage}')");
263261
}
264262

265263
if (stateAsList.Count - 1 != this.arguments.Count)
266264
{
267-
Services.ThrowException($"Incorrect template message argument count for the '{originalMessage}' template message. (Expected: '{this.arguments.Count}', Actual: '{stateAsList.Count - 1}')");
265+
throw new LoggingAssertionFailedException($"Incorrect template message argument count for the '{originalMessage}' template message. (Expected: '{this.arguments.Count}', Actual: '{stateAsList.Count - 1}')");
268266
}
269267

270268
var messageArguments = new LogMessageTemplateArguments(
@@ -278,15 +276,15 @@ public void Assert<TState>(LogLevel logLevel, TState state, Exception? exception
278276

279277
if (message != this.message)
280278
{
281-
Services.ThrowException($"Wrong log message for the Log({logLevel}) method call. (Expected: '{this.message}', Actual: '{message}')");
279+
throw new LoggingAssertionFailedException($"Wrong log message for the Log({logLevel}) method call. (Expected: '{this.message}', Actual: '{message}')");
282280
}
283281
}
284282

285283
if (this.exception != null)
286284
{
287285
if (exception is null)
288286
{
289-
Services.ThrowException($"Expected an exception but no exeception has been thrown.");
287+
throw new LoggingAssertionFailedException($"Expected an exception but no exeception has been thrown.");
290288
}
291289
else
292290
{
@@ -327,7 +325,7 @@ public override void Assert<TState>(TState state)
327325
}
328326
else
329327
{
330-
Services.ThrowException($"The 'BeginScope()' has been called with a wrong state argument type (Expected: {typeof(TExpectedState).Name}, Actual: {typeof(TState).Name}).");
328+
throw new LoggingAssertionFailedException($"The 'BeginScope()' has been called with a wrong state argument type (Expected: {typeof(TExpectedState).Name}, Actual: {typeof(TState).Name}).");
331329
}
332330
}
333331
}
@@ -381,7 +379,7 @@ public void Assert(LogLevel logLevel)
381379
{
382380
if (this.expectedLogLevel != logLevel)
383381
{
384-
Services.ThrowException($"The 'IsEnabled()' has been called with a wrong log level (Expected: {this.expectedLogLevel}, Actual: {logLevel}).");
382+
throw new LoggingAssertionFailedException($"The 'IsEnabled()' has been called with a wrong log level (Expected: {this.expectedLogLevel}, Actual: {logLevel}).");
385383
}
386384
}
387385
}

src/Logging.Assertions/LoggerMockSetupSequenceExtensions.cs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
namespace PosInformatique.Logging.Assertions
88
{
99
using System.Collections.Generic;
10-
using FluentAssertions;
1110
using Microsoft.Extensions.Logging;
1211

1312
/// <summary>
@@ -24,7 +23,7 @@ public static class LoggerMockSetupSequenceExtensions
2423
/// <returns>The current <see cref="ILoggerMockSetupSequence"/> which allows to continue the setup of the <see cref="ILogger"/> method calls.</returns>
2524
public static ILoggerMockSetupSequence BeginScope(this ILoggerMockSetupSequence sequence, object state)
2625
{
27-
return sequence.BeginScope<object>(expectedState => state.Should().BeEquivalentTo(expectedState));
26+
return sequence.BeginScope<object>(expectedState => AssertionHelper.BeEquivalentTo(ToDictionary(state), ToDictionary(expectedState)));
2827
}
2928

3029
/// <summary>
@@ -39,17 +38,7 @@ public static ILoggerMockSetupSequence BeginScope(this ILoggerMockSetupSequence
3938
/// <returns>The current <see cref="ILoggerMockSetupSequence"/> which allows to continue the setup of the <see cref="ILogger"/> method calls.</returns>
4039
public static ILoggerMockSetupSequence BeginScopeAsDictionary(this ILoggerMockSetupSequence sequence, object state)
4140
{
42-
return sequence.BeginScope<IDictionary<string, object>>(expectedState =>
43-
{
44-
var actualState = new Dictionary<string, object>();
45-
46-
foreach (var property in state.GetType().GetProperties())
47-
{
48-
actualState.Add(property.Name, property.GetValue(state));
49-
}
50-
51-
actualState.Should().BeEquivalentTo(expectedState);
52-
});
41+
return sequence.BeginScope<IDictionary<string, object>>(expectedState => AssertionHelper.BeEquivalentTo(ToDictionary(state), expectedState));
5342
}
5443

5544
/// <summary>
@@ -108,7 +97,7 @@ public static ILoggerMockSetupSequenceLog LogWarning(this ILoggerMockSetupSequen
10897
/// <returns>An instance of <see cref="ILoggerMockSetupSequence"/> which allows to continue the setup of the method calls.</returns>
10998
public static ILoggerMockSetupSequenceLog WithException(this ILoggerMockSetupSequenceError sequence, Exception expectedException)
11099
{
111-
return sequence.WithException(actualException => actualException.Should().BeSameAs(expectedException));
100+
return sequence.WithException(actualException => AssertionHelper.BeSameAs(actualException, expectedException));
112101
}
113102

114103
/// <summary>
@@ -123,9 +112,25 @@ public static ILoggerMockSetupSequence WithArguments(this ILoggerMockSetupSequen
123112
{
124113
for (int i = 0; i < expectedArguments.Length; i++)
125114
{
126-
actualArguments[i].Should().Be(expectedArguments[i]);
115+
if (!Equals(actualArguments[i], expectedArguments[i]))
116+
{
117+
throw new LoggingAssertionFailedException(
118+
$"Log message arguments at the index {i} is different.{Environment.NewLine}Expected: {AssertionHelper.ToString(expectedArguments[i])}{Environment.NewLine}Actual: {AssertionHelper.ToString(actualArguments[i])}");
119+
}
127120
}
128121
});
129122
}
123+
124+
private static IDictionary<string, object> ToDictionary(object @object)
125+
{
126+
var dictionary = new Dictionary<string, object>();
127+
128+
foreach (var property in @object.GetType().GetProperties())
129+
{
130+
dictionary.Add(property.Name, property.GetValue(@object));
131+
}
132+
133+
return dictionary;
134+
}
130135
}
131136
}

src/Logging.Assertions/Logging.Assertions.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
<PackageProjectUrl>https://github.com/PosInformatique/PosInformatique.Logging.Assertions</PackageProjectUrl>
1111
<PackageReadmeFile>README.md</PackageReadmeFile>
1212
<PackageReleaseNotes>
13+
1.6.0
14+
- Removing dependency to FluentAssertions library.
15+
1316
1.5.1
1417
- Fix a bug to check the log messages when using message templates assertions.
1518

@@ -57,7 +60,6 @@
5760
</ItemGroup>
5861

5962
<ItemGroup>
60-
<PackageReference Include="FluentAssertions" Version="6.0.0" />
6163
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.0" />
6264
</ItemGroup>
6365

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//-----------------------------------------------------------------------
2+
// <copyright file="LoggingAssertionFailedException.cs" company="P.O.S Informatique">
3+
// Copyright (c) P.O.S Informatique. All rights reserved.
4+
// </copyright>
5+
//-----------------------------------------------------------------------
6+
7+
namespace PosInformatique.Logging.Assertions
8+
{
9+
/// <summary>
10+
/// Occurs when a logging assertion has been failed.
11+
/// </summary>
12+
public class LoggingAssertionFailedException : Exception
13+
{
14+
/// <summary>
15+
/// Initializes a new instance of the <see cref="LoggingAssertionFailedException"/> class.
16+
/// </summary>
17+
public LoggingAssertionFailedException()
18+
: base()
19+
{
20+
}
21+
22+
/// <summary>
23+
/// Initializes a new instance of the <see cref="LoggingAssertionFailedException"/> class
24+
/// with the specified <paramref name="message"/>.
25+
/// </summary>
26+
/// <param name="message">Message of the exception.</param>
27+
public LoggingAssertionFailedException(string message)
28+
: base(message)
29+
{
30+
}
31+
32+
/// <summary>
33+
/// Initializes a new instance of the <see cref="LoggingAssertionFailedException"/> class
34+
/// with the specified <paramref name="message"/> and the <paramref name="innerException"/>.
35+
/// </summary>
36+
/// <param name="message">Message of the exception.</param>
37+
/// <param name="innerException">Inner exception related to the <see cref="LoggingAssertionFailedException"/> to create.</param>
38+
public LoggingAssertionFailedException(string message, Exception innerException)
39+
: base(message, innerException)
40+
{
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)