Skip to content

Commit 6ee8bdb

Browse files
authored
fix: add WithMessageNotContaining assertion for exception messages (#3165)
1 parent a635024 commit 6ee8bdb

6 files changed

+133
-0
lines changed

TUnit.Assertions.Tests/ThrowInDelegateValueAssertionTests.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,68 @@ await Assert.That(assertion)
3737
.Throws<AssertionException>()
3838
.WithMessageContaining("SYSTEM.EXCEPTION", StringComparison.OrdinalIgnoreCase);
3939
}
40+
41+
[Test]
42+
public async Task ThrowInDelegateValueAssertion_WithMessageNotContaining_Passes_WhenMessageDoesNotContainText()
43+
{
44+
var assertion = async () => await Assert.That(() =>
45+
{
46+
throw new Exception("This is an error message");
47+
return true;
48+
}).IsEqualTo(true);
49+
50+
await Assert.That(assertion)
51+
.Throws<AssertionException>()
52+
.WithMessageNotContaining("different text");
53+
}
54+
55+
[Test]
56+
public async Task ThrowInDelegateValueAssertion_WithMessageNotContaining_Fails_WhenMessageContainsText()
57+
{
58+
var assertion = async () => await Assert.That(() =>
59+
{
60+
throw new Exception("This is an error message");
61+
return true;
62+
}).IsEqualTo(true);
63+
64+
var finalAssertion = async () => await Assert.That(assertion)
65+
.Throws<AssertionException>()
66+
.WithMessageNotContaining("error message");
67+
68+
await Assert.That(finalAssertion)
69+
.Throws<AssertionException>()
70+
.WithMessageContaining("which message does not contain \"error message\"");
71+
}
72+
73+
[Test]
74+
public async Task ThrowInDelegateValueAssertion_WithMessageNotContaining_RespectsCaseInsensitive()
75+
{
76+
var assertion = async () => await Assert.That(() =>
77+
{
78+
throw new Exception("This is an ERROR message");
79+
return true;
80+
}).IsEqualTo(true);
81+
82+
var finalAssertion = async () => await Assert.That(assertion)
83+
.Throws<AssertionException>()
84+
.WithMessageNotContaining("error message", StringComparison.OrdinalIgnoreCase);
85+
86+
await Assert.That(finalAssertion)
87+
.Throws<AssertionException>()
88+
.WithMessageContaining("which message does not contain \"error message\"");
89+
}
90+
91+
[Test]
92+
public async Task ThrowInDelegateValueAssertion_WithMessageNotContaining_RespectsCaseSensitive()
93+
{
94+
var assertion = async () => await Assert.That(() =>
95+
{
96+
throw new Exception("This is an ERROR message");
97+
return true;
98+
}).IsEqualTo(true);
99+
100+
await Assert.That(assertion)
101+
.Throws<AssertionException>()
102+
.WithMessageNotContaining("error message", StringComparison.Ordinal);
103+
}
40104
}

TUnit.Assertions/Assertions/Throws/ThrowsException.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,20 @@ public ThrowsException<TActual, TException> WithMessageContaining(string expecte
5050
return this;
5151
}
5252

53+
public ThrowsException<TActual, TException> WithMessageNotContaining(string expected, [CallerArgumentExpression(nameof(expected))] string? doNotPopulateThisValue = null)
54+
{
55+
_source.RegisterAssertion(new ThrowsWithMessageNotContainingAssertCondition<TActual, TException>(expected, StringComparison.Ordinal, _selector)
56+
, [doNotPopulateThisValue]);
57+
return this;
58+
}
59+
60+
public ThrowsException<TActual, TException> WithMessageNotContaining(string expected, StringComparison stringComparison, [CallerArgumentExpression(nameof(expected))] string? doNotPopulateThisValue = null, [CallerArgumentExpression(nameof(stringComparison))] string? doNotPopulateThisValue2 = null)
61+
{
62+
_source.RegisterAssertion(new ThrowsWithMessageNotContainingAssertCondition<TActual, TException>(expected, stringComparison, _selector)
63+
, [doNotPopulateThisValue, doNotPopulateThisValue2]);
64+
return this;
65+
}
66+
5367
public ThrowsException<TActual, Exception> WithInnerException()
5468
{
5569
_source.AppendExpression($"{nameof(WithInnerException)}()");
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using TUnit.Assertions.Extensions;
2+
3+
namespace TUnit.Assertions.AssertConditions.Throws;
4+
5+
public class ThrowsWithMessageNotContainingAssertCondition<TActual, TException>(
6+
string expected,
7+
StringComparison stringComparison,
8+
Func<Exception?, Exception?> exceptionSelector)
9+
: DelegateAssertCondition<TActual, Exception>
10+
where TException : Exception
11+
{
12+
internal protected override string GetExpectation()
13+
=> $"to throw {typeof(TException).Name.PrependAOrAn()} which message does not contain \"{expected?.ShowNewLines().TruncateWithEllipsis(100)}\"";
14+
15+
protected override ValueTask<AssertionResult> GetResult(
16+
TActual? actualValue, Exception? exception,
17+
AssertionMetadata assertionMetadata
18+
)
19+
{
20+
var actualException = exceptionSelector(exception);
21+
22+
return AssertionResult
23+
.FailIf(actualException is null,
24+
"the exception is null")
25+
.OrFailIf(actualException is not null && actualException.Message.Contains(expected, stringComparison),
26+
$"found \"{actualException?.Message.ShowNewLines().TruncateWithEllipsis(100)}\"");
27+
}
28+
}

TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,13 @@ namespace .
625625
protected override string GetExpectation() { }
626626
protected override .<.> GetResult(TActual? actualValue, ? exception, .AssertionMetadata assertionMetadata) { }
627627
}
628+
public class ThrowsWithMessageNotContainingAssertCondition<TActual, TException> : .<TActual, >
629+
where TException :
630+
{
631+
public ThrowsWithMessageNotContainingAssertCondition(string expected, stringComparison, <?, ?> exceptionSelector) { }
632+
protected override string GetExpectation() { }
633+
protected override .<.> GetResult(TActual? actualValue, ? exception, .AssertionMetadata assertionMetadata) { }
634+
}
628635
public class ThrowsWithParamNameAssertCondition<TActual, TException> : .<TActual, >
629636
where TException :
630637
{
@@ -2734,6 +2741,8 @@ namespace .Extensions
27342741
public .<TActual, TException> WithMessageContaining(string expected, [.("expected")] string? doNotPopulateThisValue = null) { }
27352742
public .<TActual, TException> WithMessageContaining(string expected, stringComparison, [.("expected")] string? doNotPopulateThisValue = null, [.("stringComparison")] string? doNotPopulateThisValue2 = null) { }
27362743
public .<TActual, TException> WithMessageMatching(. match, [.("match")] string? doNotPopulateThisValue = null) { }
2744+
public .<TActual, TException> WithMessageNotContaining(string expected, [.("expected")] string? doNotPopulateThisValue = null) { }
2745+
public .<TActual, TException> WithMessageNotContaining(string expected, stringComparison, [.("expected")] string? doNotPopulateThisValue = null, [.("stringComparison")] string? doNotPopulateThisValue2 = null) { }
27372746
public static .<TException?> op_Explicit(.<TActual, TException> throwsException) { }
27382747
}
27392748
public static class ThrowsExtensions

TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,13 @@ namespace .
625625
protected override string GetExpectation() { }
626626
protected override .<.> GetResult(TActual? actualValue, ? exception, .AssertionMetadata assertionMetadata) { }
627627
}
628+
public class ThrowsWithMessageNotContainingAssertCondition<TActual, TException> : .<TActual, >
629+
where TException :
630+
{
631+
public ThrowsWithMessageNotContainingAssertCondition(string expected, stringComparison, <?, ?> exceptionSelector) { }
632+
protected override string GetExpectation() { }
633+
protected override .<.> GetResult(TActual? actualValue, ? exception, .AssertionMetadata assertionMetadata) { }
634+
}
628635
public class ThrowsWithParamNameAssertCondition<TActual, TException> : .<TActual, >
629636
where TException :
630637
{
@@ -2734,6 +2741,8 @@ namespace .Extensions
27342741
public .<TActual, TException> WithMessageContaining(string expected, [.("expected")] string? doNotPopulateThisValue = null) { }
27352742
public .<TActual, TException> WithMessageContaining(string expected, stringComparison, [.("expected")] string? doNotPopulateThisValue = null, [.("stringComparison")] string? doNotPopulateThisValue2 = null) { }
27362743
public .<TActual, TException> WithMessageMatching(. match, [.("match")] string? doNotPopulateThisValue = null) { }
2744+
public .<TActual, TException> WithMessageNotContaining(string expected, [.("expected")] string? doNotPopulateThisValue = null) { }
2745+
public .<TActual, TException> WithMessageNotContaining(string expected, stringComparison, [.("expected")] string? doNotPopulateThisValue = null, [.("stringComparison")] string? doNotPopulateThisValue2 = null) { }
27372746
public static .<TException?> op_Explicit(.<TActual, TException> throwsException) { }
27382747
}
27392748
public static class ThrowsExtensions

TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,13 @@ namespace .
603603
protected override string GetExpectation() { }
604604
protected override .<.> GetResult(TActual? actualValue, ? exception, .AssertionMetadata assertionMetadata) { }
605605
}
606+
public class ThrowsWithMessageNotContainingAssertCondition<TActual, TException> : .<TActual, >
607+
where TException :
608+
{
609+
public ThrowsWithMessageNotContainingAssertCondition(string expected, stringComparison, <?, ?> exceptionSelector) { }
610+
protected override string GetExpectation() { }
611+
protected override .<.> GetResult(TActual? actualValue, ? exception, .AssertionMetadata assertionMetadata) { }
612+
}
606613
public class ThrowsWithParamNameAssertCondition<TActual, TException> : .<TActual, >
607614
where TException :
608615
{
@@ -2590,6 +2597,8 @@ namespace .Extensions
25902597
public .<TActual, TException> WithMessageContaining(string expected, [.("expected")] string? doNotPopulateThisValue = null) { }
25912598
public .<TActual, TException> WithMessageContaining(string expected, stringComparison, [.("expected")] string? doNotPopulateThisValue = null, [.("stringComparison")] string? doNotPopulateThisValue2 = null) { }
25922599
public .<TActual, TException> WithMessageMatching(. match, [.("match")] string? doNotPopulateThisValue = null) { }
2600+
public .<TActual, TException> WithMessageNotContaining(string expected, [.("expected")] string? doNotPopulateThisValue = null) { }
2601+
public .<TActual, TException> WithMessageNotContaining(string expected, stringComparison, [.("expected")] string? doNotPopulateThisValue = null, [.("stringComparison")] string? doNotPopulateThisValue2 = null) { }
25932602
public static .<TException?> op_Explicit(.<TActual, TException> throwsException) { }
25942603
}
25952604
public static class ThrowsExtensions

0 commit comments

Comments
 (0)