Skip to content

Commit 7610317

Browse files
authored
fix(assertions): add nullability checks for [AssertionFrom<T>] generated assertions (#3426)
1 parent 865552d commit 7610317

File tree

2 files changed

+113
-1
lines changed

2 files changed

+113
-1
lines changed

TUnit.Assertions.SourceGenerator/Generators/AssertionMethodGenerator.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,28 @@ private static void GenerateAssertConditionClassForMethod(SourceProductionContex
699699
sourceBuilder.AppendLine();
700700
}
701701

702-
if (!attributeData.TargetType.IsValueType)
702+
// Check if the method's first parameter accepts null values
703+
// For static methods like string.IsNullOrEmpty(string? value), the first parameter
704+
// is the value being asserted. If it's marked as nullable (NullableAnnotation.Annotated),
705+
// we should skip the null check and let the method handle null values.
706+
var shouldGenerateNullCheck = !attributeData.TargetType.IsValueType;
707+
if (shouldGenerateNullCheck && staticMethod.Parameters.Length > 0)
708+
{
709+
var firstParameter = staticMethod.Parameters[0];
710+
// Skip null check if the parameter explicitly accepts null (e.g., string? value)
711+
if (firstParameter.NullableAnnotation == NullableAnnotation.Annotated)
712+
{
713+
shouldGenerateNullCheck = false;
714+
}
715+
// For backwards compatibility with .NET Framework where NullableAnnotation might not be set,
716+
// also check for well-known methods that accept null by design
717+
else if (methodName == "IsNullOrEmpty" || methodName == "IsNullOrWhiteSpace")
718+
{
719+
shouldGenerateNullCheck = false;
720+
}
721+
}
722+
723+
if (shouldGenerateNullCheck)
703724
{
704725
sourceBuilder.AppendLine(" if (actualValue is null)");
705726
sourceBuilder.AppendLine(" {");
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using TUnit.Assertions.Extensions;
2+
3+
namespace TUnit.Assertions.Tests;
4+
5+
/// <summary>
6+
/// Tests for string assertions that accept null values (IsNullOrEmpty, IsNullOrWhiteSpace)
7+
/// </summary>
8+
public class StringNullabilityAssertionTests
9+
{
10+
[Test]
11+
public async Task IsNullOrEmpty_WithNullString_Passes()
12+
{
13+
string? nullString = null;
14+
await Assert.That(nullString).IsNullOrEmpty();
15+
}
16+
17+
[Test]
18+
public async Task IsNullOrEmpty_WithEmptyString_Passes()
19+
{
20+
var emptyString = "";
21+
await Assert.That(emptyString).IsNullOrEmpty();
22+
}
23+
24+
[Test]
25+
public async Task IsNullOrEmpty_WithNonEmptyString_Fails()
26+
{
27+
var value = "Hello";
28+
await Assert.That(async () => await Assert.That(value).IsNullOrEmpty())
29+
.Throws<AssertionException>();
30+
}
31+
32+
[Test]
33+
public async Task IsNullOrEmpty_WithWhitespace_Fails()
34+
{
35+
var value = " ";
36+
await Assert.That(async () => await Assert.That(value).IsNullOrEmpty())
37+
.Throws<AssertionException>();
38+
}
39+
40+
[Test]
41+
public async Task IsNullOrWhiteSpace_WithNullString_Passes()
42+
{
43+
string? nullString = null;
44+
await Assert.That(nullString).IsNullOrWhiteSpace();
45+
}
46+
47+
[Test]
48+
public async Task IsNullOrWhiteSpace_WithEmptyString_Passes()
49+
{
50+
var emptyString = "";
51+
await Assert.That(emptyString).IsNullOrWhiteSpace();
52+
}
53+
54+
[Test]
55+
public async Task IsNullOrWhiteSpace_WithWhitespace_Passes()
56+
{
57+
var whitespace = " ";
58+
await Assert.That(whitespace).IsNullOrWhiteSpace();
59+
}
60+
61+
[Test]
62+
public async Task IsNullOrWhiteSpace_WithNonEmptyString_Fails()
63+
{
64+
var value = "Hello";
65+
await Assert.That(async () => await Assert.That(value).IsNullOrWhiteSpace())
66+
.Throws<AssertionException>();
67+
}
68+
69+
[Test]
70+
public async Task IsNotNullOrEmpty_WithNullString_Fails()
71+
{
72+
string? nullString = null;
73+
await Assert.That(async () => await Assert.That(nullString).IsNotNullOrEmpty())
74+
.Throws<AssertionException>();
75+
}
76+
77+
[Test]
78+
public async Task IsNotNullOrEmpty_WithEmptyString_Fails()
79+
{
80+
var emptyString = "";
81+
await Assert.That(async () => await Assert.That(emptyString).IsNotNullOrEmpty())
82+
.Throws<AssertionException>();
83+
}
84+
85+
[Test]
86+
public async Task IsNotNullOrEmpty_WithNonEmptyString_Passes()
87+
{
88+
var value = "Hello";
89+
await Assert.That(value).IsNotNullOrEmpty();
90+
}
91+
}

0 commit comments

Comments
 (0)