Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions TUnit.Assertions.Tests/Old/DefaultAssertionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,122 @@ public async Task IsNotDefault_ReferenceType_Object_NotNull()
object obj = new object();
await TUnitAssert.That(obj).IsNotDefault();
}

// ============ NULLABLE VALUE TYPE TESTS ============

[Test]
public async Task IsDefault_NullableValueType_Bool_Null()
{
bool? value = null;
await TUnitAssert.That(value).IsDefault();
}

[Test]
public async Task IsDefault_NullableValueType_Bool_HasValue()
{
await TUnitAssert.That(async () =>
{
bool? value = false;
await TUnitAssert.That(value).IsDefault();
}).Throws<TUnitAssertionException>();
}

[Test]
public async Task IsDefault_NullableValueType_Int_Null()
{
int? value = null;
await TUnitAssert.That(value).IsDefault();
}

[Test]
public async Task IsDefault_NullableValueType_Int_HasValue()
{
await TUnitAssert.That(async () =>
{
int? value = 42;
await TUnitAssert.That(value).IsDefault();
}).Throws<TUnitAssertionException>();
}

[Test]
public async Task IsDefault_NullableValueType_DateTime_Null()
{
DateTime? value = null;
await TUnitAssert.That(value).IsDefault();
}

[Test]
public async Task IsDefault_NullableValueType_DateTime_HasValue()
{
await TUnitAssert.That(async () =>
{
DateTime? value = DateTime.Now;
await TUnitAssert.That(value).IsDefault();
}).Throws<TUnitAssertionException>();
}

[Test]
public async Task IsNotDefault_NullableValueType_Bool_Null()
{
await TUnitAssert.That(async () =>
{
bool? value = null;
await TUnitAssert.That(value).IsNotDefault();
}).Throws<TUnitAssertionException>();
}

[Test]
public async Task IsNotDefault_NullableValueType_Bool_True()
{
bool? value = true;
await TUnitAssert.That(value).IsNotDefault();
}

[Test]
public async Task IsNotDefault_NullableValueType_Bool_False()
{
bool? value = false;
await TUnitAssert.That(value).IsNotDefault();
}

[Test]
public async Task IsNotDefault_NullableValueType_Int_Null()
{
await TUnitAssert.That(async () =>
{
int? value = null;
await TUnitAssert.That(value).IsNotDefault();
}).Throws<TUnitAssertionException>();
}

[Test]
public async Task IsNotDefault_NullableValueType_Int_Zero()
{
int? value = 0;
await TUnitAssert.That(value).IsNotDefault();
}

[Test]
public async Task IsNotDefault_NullableValueType_Int_NonZero()
{
int? value = 42;
await TUnitAssert.That(value).IsNotDefault();
}

[Test]
public async Task IsNotDefault_NullableValueType_DateTime_Null()
{
await TUnitAssert.That(async () =>
{
DateTime? value = null;
await TUnitAssert.That(value).IsNotDefault();
}).Throws<TUnitAssertionException>();
}

[Test]
public async Task IsNotDefault_NullableValueType_DateTime_HasValue()
{
DateTime? value = DateTime.Now;
await TUnitAssert.That(value).IsNotDefault();
}
}
68 changes: 68 additions & 0 deletions TUnit.Assertions/Conditions/NullAssertion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,74 @@ protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<TValue> m
protected override string GetExpectation() => $"to not be default({typeof(TValue).Name})";
}

/// <summary>
/// Asserts that a nullable value type is equal to the default value (null).
/// For nullable value types like bool?, int?, this checks if the value is null.
/// </summary>
[AssertionExtension("IsDefault")]
public class IsDefaultNullableAssertion<TValue> : Assertion<TValue?> where TValue : struct
{
public IsDefaultNullableAssertion(
AssertionContext<TValue?> context)
: base(context)
{
}

protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<TValue?> metadata)
{
var value = metadata.Value;
var exception = metadata.Exception;

if (exception != null)
{
return Task.FromResult(AssertionResult.Failed($"threw {exception.GetType().Name}"));
}

if (!value.HasValue)
{
return Task.FromResult(AssertionResult.Passed);
}

return Task.FromResult(AssertionResult.Failed($"value is {value}"));
}

protected override string GetExpectation() => $"to be default({typeof(TValue).Name}?)";
}

/// <summary>
/// Asserts that a nullable value type is not the default value (not null).
/// For nullable value types like bool?, int?, this checks if the value has a value.
/// </summary>
[AssertionExtension("IsNotDefault")]
public class IsNotDefaultNullableAssertion<TValue> : Assertion<TValue?> where TValue : struct
{
public IsNotDefaultNullableAssertion(
AssertionContext<TValue?> context)
: base(context)
{
}

protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<TValue?> metadata)
{
var value = metadata.Value;
var exception = metadata.Exception;

if (exception != null)
{
return Task.FromResult(AssertionResult.Failed($"threw {exception.GetType().Name}"));
}

if (value.HasValue)
{
return Task.FromResult(AssertionResult.Passed);
}

return Task.FromResult(AssertionResult.Failed($"value is default({typeof(TValue).Name}?)"));
}

protected override string GetExpectation() => $"to not be default({typeof(TValue).Name}?)";
}

/// <summary>
/// Asserts that a reference type value is equal to the default value (null).
/// For reference types, this is equivalent to IsNull().
Expand Down
66 changes: 0 additions & 66 deletions TUnit.Assertions/Extensions/AssertionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ namespace TUnit.Assertions.Extensions;
/// </summary>
public static class AssertionExtensions
{
// ============ NULL CHECKS ============
// IsNull is generated by AssertionExtensionGenerator
// IsNotNull is manually implemented below for proper nullability inference

/// <summary>
/// Asserts that the value is not null (for nullable reference types).
/// Returns a non-nullable assertion allowing proper nullability flow analysis.
Expand Down Expand Up @@ -66,9 +62,6 @@ public static CollectionNotNullAssertion<TCollection, TItem> IsNotNull<TCollecti
return new CollectionNotNullAssertion<TCollection, TItem>(mappedContext);
}

// ============ EQUALITY ============
// IsEqualTo methods are now generated by AssertionExtensionGenerator for all assertion types

/// <summary>
/// Alias for IsEqualTo - asserts that the value is equal to the expected value.
/// Works with assertions, And, and Or continuations!
Expand All @@ -82,8 +75,6 @@ public static EqualsAssertion<TValue> EqualTo<TValue>(
return new EqualsAssertion<TValue>(source.Context, expected);
}

// IsNotEqualTo<TValue> is now generated by AssertionExtensionGenerator

/// <summary>
/// Asserts that the string is equal to the expected value using the specified comparison.
/// Uses .WithComparison() since StringEqualsAssertion doesn't have a constructor for this.
Expand All @@ -99,11 +90,6 @@ public static StringEqualsAssertion IsEqualTo(
return assertion.WithComparison(comparison);
}


// ============ COMPARISONS ============
// IsGreaterThan, IsGreaterThanOrEqualTo, IsLessThan, IsLessThanOrEqualTo, and IsBetween
// are now generated by AssertionExtensionGenerator

/// <summary>
/// Asserts that the numeric value is greater than zero (positive).
/// </summary>
Expand Down Expand Up @@ -166,12 +152,6 @@ public static LessThanAssertion<TValue> IsNegative<TValue>(
return new LessThanAssertion<TValue>(mappedContext, default(TValue)!);
}

// ============ BOOLEAN ============
// IsTrue and IsFalse are now generated by AssertionExtensionGenerator for IAssertionSource<bool>

// ============ TYPE CHECKS ============
// IsTypeOf(Type), IsAssignableTo, IsNotAssignableTo are now generated by AssertionExtensionGenerator

/// <summary>
/// Asserts that the value is of the specified type and returns an assertion on the casted value.
/// This extension method variant requires specifying both TExpected and TValue type parameters.
Expand Down Expand Up @@ -330,13 +310,6 @@ private static string GetMemberPath<TObject, TMember>(Expression<Func<TObject, T
return parts.Count > 0 ? string.Join(".", parts) : "Unknown";
}

// ============ REFERENCE EQUALITY ============
// IsSameReferenceAs and IsNotSameReferenceAs are now generated by AssertionExtensionGenerator

// ============ STRING ASSERTIONS ============
// All string-specific assertion methods are now generated by AssertionExtensionGenerator
// from their respective assertion classes (StringContainsAssertion, StringStartsWithAssertion, etc.)

/// <summary>
/// Returns a wrapper for string length assertions.
/// Example: await Assert.That(str).HasLength().EqualTo(5);
Expand All @@ -361,20 +334,6 @@ public static StringLengthAssertion HasLength(
return new StringLengthAssertion(source.Context, expectedLength);
}

// ============ DICTIONARY ASSERTIONS ============
// Dictionary assertions (ContainsKey, DoesNotContainKey) are now generated by AssertionExtensionGenerator
// from the dictionary assertion classes decorated with [AssertionExtension] attributes.
// Use .Using(comparer) to specify a custom equality comparer for key comparison.

// ============ COLLECTION ASSERTIONS ============
// Collection assertions are now generated by AssertionExtensionGenerator
// from the collection assertion classes decorated with [AssertionExtension] attributes.
// This eliminates the need for manual overloads with OverloadResolutionPriority workarounds.

// Collection equivalence assertions (IsEquivalentTo/IsNotEquivalentTo for IEnumerable<T>)
// are now generated by AssertionExtensionGenerator.
// Use .Using(comparer) to specify a custom equality comparer.

/// <summary>
/// Asserts that the value is structurally equivalent to the expected value.
/// Performs deep comparison of properties and fields.
Expand Down Expand Up @@ -403,8 +362,6 @@ public static NotStructuralEquivalencyAssertion<TValue> IsNotEquivalentTo<TValue
return new NotStructuralEquivalencyAssertion<TValue>(source.Context, expected, expression);
}

// ============ PREDICATE CHECKS ============

/// <summary>
/// Asserts that the value satisfies the specified predicate.
/// Example: await Assert.That(x).Satisfies(v => v > 0 && v < 100);
Expand Down Expand Up @@ -456,12 +413,6 @@ public static AsyncMappedSatisfiesAssertion<TValue, TMapped> Satisfies<TValue, T
selectorExpression ?? "selector");
}

// IsEquatableOrEqualTo is now generated by AssertionExtensionGenerator

// ============ MEMBERSHIP CHECKS ============
// IsIn and IsNotIn with IEnumerable are now generated by AssertionExtensionGenerator
// Params overloads remain as convenience methods

/// <summary>
/// Asserts that the value is in the specified collection (params array convenience method).
/// Example: await Assert.That(5).IsIn(1, 3, 5, 7, 9);
Expand All @@ -486,8 +437,6 @@ public static IsNotInAssertion<TValue> IsNotIn<TValue>(
return new IsNotInAssertion<TValue>(source.Context, collection);
}

// ============ EXCEPTION CHECKS ============

/// <summary>
/// Asserts that the delegate throws the specified exception type (or subclass).
/// Only available on delegate-based assertions for type safety.
Expand Down Expand Up @@ -571,9 +520,6 @@ public static ThrowsNothingAssertion<TValue> ThrowsNothing<TValue>(
return new ThrowsNothingAssertion<TValue>(source.Context);
}

// ============ EXCEPTION MESSAGE/PROPERTY ASSERTIONS ============
// These work on IAssertionSource<TException> where the exception is the value

/// <summary>
/// Asserts that the exception message contains the specified substring.
/// Works after Throws assertions.
Expand Down Expand Up @@ -742,7 +688,6 @@ public static ExceptionParameterNameAssertion<TException> WithParameterName<TExc
return new ExceptionParameterNameAssertion<TException>(source.Context, expectedParameterName);
}

// Specific overloads for delegate types where TValue is always object?
public static ThrowsAssertion<TException> Throws<TException>(this DelegateAssertion source) where TException : Exception
{
var iface = (IAssertionSource<object?>)source;
Expand Down Expand Up @@ -1037,10 +982,6 @@ public static LessThanOrEqualAssertion<TimeOnly> IsBeforeOrEqualTo(
}
#endif

// IsDefault and IsNotDefault are now generated by AssertionExtensionGenerator

// ============ TIMING ASSERTIONS ============

/// <summary>
/// Asserts that a synchronous delegate completes execution within the specified timeout.
/// If the delegate takes longer than the timeout, the assertion fails.
Expand Down Expand Up @@ -1081,8 +1022,6 @@ private static Func<Task> GetFuncFromAsyncDelegate(AsyncDelegateAssertion source
return source.AsyncAction;
}

// ============ PARSING ASSERTIONS ============

/// <summary>
/// Asserts that a string can be parsed into the specified type.
/// </summary>
Expand Down Expand Up @@ -1114,8 +1053,6 @@ private static Func<Task> GetFuncFromAsyncDelegate(AsyncDelegateAssertion source
return new Assertions.Strings.WhenParsedIntoAssertion<T>(source.Context);
}

// ============ ENUM ASSERTIONS ============

/// <summary>
/// Asserts that a flags enum has the specified flag set.
/// </summary>
Expand Down Expand Up @@ -1218,7 +1155,6 @@ public static Assertions.Enums.DoesNotHaveSameValueAsAssertion<TEnum> DoesNotHav

/// <summary>
/// Asserts that a value's type is assignable to a specific type (specialized for object).
/// Manual extension - should be auto-generated when generator works.
/// </summary>
public static IsAssignableToAssertion<TTarget, object> IsAssignableTo<TTarget>(
this IAssertionSource<object> source)
Expand All @@ -1229,7 +1165,6 @@ public static IsAssignableToAssertion<TTarget, object> IsAssignableTo<TTarget>(

/// <summary>
/// Asserts that a value's type is assignable to a specific type.
/// Manual extension - should be auto-generated when generator works.
/// </summary>
public static IsAssignableToAssertion<TTarget, TValue> IsAssignableTo<TTarget, TValue>(
this IAssertionSource<TValue> source)
Expand All @@ -1240,7 +1175,6 @@ public static IsAssignableToAssertion<TTarget, TValue> IsAssignableTo<TTarget, T

/// <summary>
/// Asserts that a value's type is NOT assignable to a specific type.
/// Manual extension - should be auto-generated when generator works.
/// </summary>
public static IsNotAssignableToAssertion<TTarget, TValue> IsNotAssignableTo<TTarget, TValue>(
this IAssertionSource<TValue> source)
Expand Down
2 changes: 1 addition & 1 deletion TUnit.Assertions/Sources/DelegateAssertion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public DelegateAssertion(Action action, string? expression)
{
try
{
await Task.Run(action);
action();
return (null, null);
}
catch (Exception ex)
Expand Down
Loading
Loading