diff --git a/TUnit.Assertions.Tests/TypeAssertionAmbiguityTests.cs b/TUnit.Assertions.Tests/TypeAssertionAmbiguityTests.cs index 629695faea..bdee6e99ea 100644 --- a/TUnit.Assertions.Tests/TypeAssertionAmbiguityTests.cs +++ b/TUnit.Assertions.Tests/TypeAssertionAmbiguityTests.cs @@ -150,6 +150,169 @@ await Assert.That(async () => }).Throws(); } + // ============ IsAssignableFrom TESTS ============ + + [Test] + public async Task IsAssignableFrom_DerivedToBase_Passes() + { + IElement element = new Element(); + await Assert.That(element).IsAssignableFrom(); + } + + [Test] + public async Task IsAssignableFrom_ExactType_Passes() + { + Element element = new Element(); + await Assert.That(element).IsAssignableFrom(); + } + + [Test] + public async Task IsAssignableFrom_BaseAcceptsDerived_Passes() + { + Element element = new Element(); + await Assert.That(element).IsAssignableFrom(); + } + + [Test] + public async Task IsAssignableFrom_InterfaceAcceptsDerived_Passes() + { + IElement element = new Element(); + await Assert.That(element).IsAssignableFrom(); + } + + [Test] + public async Task IsAssignableFrom_Fails_WhenNotAssignable() + { + await Assert.That(async () => + { + DerivedElement derived = new DerivedElement(); + await Assert.That(derived).IsAssignableFrom(); + }).Throws(); + } + + [Test] + public async Task IsAssignableFrom_ObjectVariable_Passes() + { + object obj = new Element(); + await Assert.That(obj).IsAssignableFrom(); + } + + // ============ IsNotAssignableFrom TESTS ============ + + [Test] + public async Task IsNotAssignableFrom_UnrelatedTypes_Passes() + { + DerivedElement derived = new DerivedElement(); + await Assert.That(derived).IsNotAssignableFrom(); + } + + [Test] + public async Task IsNotAssignableFrom_DerivedFromBase_Passes() + { + DerivedElement derived = new DerivedElement(); + await Assert.That(derived).IsNotAssignableFrom(); + } + + [Test] + public async Task IsNotAssignableFrom_Fails_WhenAssignable() + { + await Assert.That(async () => + { + Element element = new Element(); + await Assert.That(element).IsNotAssignableFrom(); + }).Throws(); + } + + // ============ IsAssignableFrom EDGE CASE TESTS ============ + + [Test] + public async Task IsAssignableFrom_Fails_WhenNull() + { + await Assert.That(async () => + { + Element? element = null; + await Assert.That(element).IsAssignableFrom(); + }).Throws(); + } + + [Test] + public async Task IsNotAssignableFrom_Fails_WhenNull() + { + await Assert.That(async () => + { + Element? element = null; + await Assert.That(element).IsNotAssignableFrom(); + }).Throws(); + } + + [Test] + public async Task IsAssignableFrom_WithException_ChecksExceptionType() + { + await Assert.That(async () => + { + throw new InvalidOperationException("test"); + }).Throws() + .And + .IsAssignableFrom(); + } + + [Test] + public async Task IsNotAssignableFrom_WithException_ChecksExceptionType() + { + await Assert.That(async () => + { + throw new InvalidOperationException("test"); + }).Throws() + .And + .IsNotAssignableFrom(); + } + + // ============ IsAssignableFrom CHAINING TESTS ============ + + [Test] + public async Task IsAssignableFrom_Chained_NoAmbiguity() + { + IElement element = new Element(); + await Assert.That(element) + .IsNotNull() + .And + .IsAssignableFrom() + .And + .IsAssignableFrom(); + } + + [Test] + public async Task IsNotAssignableFrom_Chained_NoAmbiguity() + { + DerivedElement derived = new DerivedElement(); + await Assert.That(derived) + .IsNotNull() + .And + .IsNotAssignableFrom() + .And + .IsNotAssignableFrom(); + } + + // ============ IsAssignableFrom IN MEMBER LAMBDA TESTS ============ + + [Test] + public async Task IsAssignableFrom_InMemberLambda_NoAmbiguity() + { + var container = new Container { ElementProperty = new Element() }; + + await Assert.That(container) + .Member(c => c.ElementProperty, assert => assert.IsAssignableFrom()); + } + + [Test] + public async Task IsNotAssignableFrom_InMemberLambda_NoAmbiguity() + { + var container = new Container { ElementProperty = new Element() }; + + await Assert.That(container) + .Member(c => c.ElementProperty, assert => assert.IsNotAssignableFrom()); + } + // ============ ISSUE #3737 REGRESSION TESTS ============ [Test] @@ -320,6 +483,27 @@ await Assert.That(obj) .IsNotAssignableTo(); } + [Test] + public async Task AllSixMethods_InSingleChain_NoAmbiguity() + { + object obj = new Element(); + + await Assert.That(obj) + .IsNotNull() + .And + .IsNotTypeOf() + .And + .IsTypeOf() + .And + .IsAssignableTo() + .And + .IsNotAssignableTo() + .And + .IsAssignableFrom() + .And + .IsNotAssignableFrom(); + } + // ============ LAMBDA ASSERTION CONTEXTS (.Member) ============ private class Container diff --git a/TUnit.Assertions/Assertions/PropertyAssertion.cs b/TUnit.Assertions/Assertions/PropertyAssertion.cs index 5b167cf11e..e5f8df59e8 100644 --- a/TUnit.Assertions/Assertions/PropertyAssertion.cs +++ b/TUnit.Assertions/Assertions/PropertyAssertion.cs @@ -133,6 +133,18 @@ public IsNotAssignableToAssertion IsNotAssignableTo() return new IsNotAssignableToAssertion(Context); } + public IsAssignableFromAssertion IsAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TSource).Name}>()"); + return new IsAssignableFromAssertion(Context); + } + + public IsNotAssignableFromAssertion IsNotAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TSource).Name}>()"); + return new IsNotAssignableFromAssertion(Context); + } + /// /// Asserts that the parent object is NOT of the specified type. /// Example: await Assert.That(obj).HasProperty(x => x.Name).IsEqualTo("test").IsNotTypeOf(); diff --git a/TUnit.Assertions/Assertions/Strings/ParseAssertions.cs b/TUnit.Assertions/Assertions/Strings/ParseAssertions.cs index 6de293bcf0..b39dea5aad 100644 --- a/TUnit.Assertions/Assertions/Strings/ParseAssertions.cs +++ b/TUnit.Assertions/Assertions/Strings/ParseAssertions.cs @@ -265,6 +265,18 @@ public IsNotAssignableToAssertion IsNotAssignableTo() return new IsNotAssignableToAssertion(Context); } + public IsAssignableFromAssertion IsAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TSource).Name}>()"); + return new IsAssignableFromAssertion(Context); + } + + public IsNotAssignableFromAssertion IsNotAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TSource).Name}>()"); + return new IsNotAssignableFromAssertion(Context); + } + /// /// Asserts that the parsed value is NOT of the specified type. /// Example: await Assert.That("123").WhenParsedInto().IsNotTypeOf(); diff --git a/TUnit.Assertions/Conditions/ListAssertions.cs b/TUnit.Assertions/Conditions/ListAssertions.cs index eee34331dc..0a7b0e9d6f 100644 --- a/TUnit.Assertions/Conditions/ListAssertions.cs +++ b/TUnit.Assertions/Conditions/ListAssertions.cs @@ -158,6 +158,20 @@ public IsNotAssignableToAssertion IsNotAssignableTo Context.ExpressionBuilder.Append($".IsNotAssignableTo<{typeof(TExpected).Name}>()"); return new IsNotAssignableToAssertion(Context); } + + /// + public IsAssignableFromAssertion IsAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TExpected).Name}>()"); + return new IsAssignableFromAssertion(Context); + } + + /// + public IsNotAssignableFromAssertion IsNotAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TExpected).Name}>()"); + return new IsNotAssignableFromAssertion(Context); + } } /// @@ -259,6 +273,20 @@ public IsNotAssignableToAssertion IsNotAssignableTo Context.ExpressionBuilder.Append($".IsNotAssignableTo<{typeof(TExpected).Name}>()"); return new IsNotAssignableToAssertion(Context); } + + /// + public IsAssignableFromAssertion IsAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TExpected).Name}>()"); + return new IsAssignableFromAssertion(Context); + } + + /// + public IsNotAssignableFromAssertion IsNotAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TExpected).Name}>()"); + return new IsNotAssignableFromAssertion(Context); + } } /// diff --git a/TUnit.Assertions/Conditions/MemberAssertion.cs b/TUnit.Assertions/Conditions/MemberAssertion.cs index 1e06c83f86..b8ae95b528 100644 --- a/TUnit.Assertions/Conditions/MemberAssertion.cs +++ b/TUnit.Assertions/Conditions/MemberAssertion.cs @@ -169,6 +169,18 @@ public IsNotAssignableToAssertion IsNotAssignableTo() return new IsNotAssignableToAssertion(Context); } + public IsAssignableFromAssertion IsAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TSource).Name}>()"); + return new IsAssignableFromAssertion(Context); + } + + public IsNotAssignableFromAssertion IsNotAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TSource).Name}>()"); + return new IsNotAssignableFromAssertion(Context); + } + /// /// Asserts that the value is NOT of the specified type. /// Example: await Assert.That(obj).Member(x => x.Property).Satisfies(val => val.IsNotTypeOf()); diff --git a/TUnit.Assertions/Conditions/ReadOnlyListAssertions.cs b/TUnit.Assertions/Conditions/ReadOnlyListAssertions.cs index ead5c113ff..9cade6088f 100644 --- a/TUnit.Assertions/Conditions/ReadOnlyListAssertions.cs +++ b/TUnit.Assertions/Conditions/ReadOnlyListAssertions.cs @@ -160,6 +160,20 @@ public IsNotAssignableToAssertion IsNotAssignableTo Context.ExpressionBuilder.Append($".IsNotAssignableTo<{typeof(TExpected).Name}>()"); return new IsNotAssignableToAssertion(Context); } + + /// + public IsAssignableFromAssertion IsAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TExpected).Name}>()"); + return new IsAssignableFromAssertion(Context); + } + + /// + public IsNotAssignableFromAssertion IsNotAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TExpected).Name}>()"); + return new IsNotAssignableFromAssertion(Context); + } } /// @@ -261,6 +275,20 @@ public IsNotAssignableToAssertion IsNotAssignableTo Context.ExpressionBuilder.Append($".IsNotAssignableTo<{typeof(TExpected).Name}>()"); return new IsNotAssignableToAssertion(Context); } + + /// + public IsAssignableFromAssertion IsAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TExpected).Name}>()"); + return new IsAssignableFromAssertion(Context); + } + + /// + public IsNotAssignableFromAssertion IsNotAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TExpected).Name}>()"); + return new IsNotAssignableFromAssertion(Context); + } } /// diff --git a/TUnit.Assertions/Conditions/TypeOfAssertion.cs b/TUnit.Assertions/Conditions/TypeOfAssertion.cs index d5d4cceea7..7682f94b87 100644 --- a/TUnit.Assertions/Conditions/TypeOfAssertion.cs +++ b/TUnit.Assertions/Conditions/TypeOfAssertion.cs @@ -190,6 +190,103 @@ protected override Task CheckAsync(EvaluationMetadata m protected override string GetExpectation() => $"to not be assignable to {_targetType.Name}"; } +/// +/// Asserts that a value's type is assignable from a specific type. +/// Checks: actualType.IsAssignableFrom(typeof(TSource)) +/// This means "can a value of type TSource be assigned to a variable of this value's type?" +/// +public class IsAssignableFromAssertion : Assertion +{ + private readonly Type _sourceType; + + public IsAssignableFromAssertion( + AssertionContext context) + : base(context) + { + _sourceType = typeof(TSource); + } + + protected override Task CheckAsync(EvaluationMetadata metadata) + { + var value = metadata.Value; + var exception = metadata.Exception; + + object? objectToCheck = null; + + if (exception != null) + { + objectToCheck = exception; + } + else if (value != null) + { + objectToCheck = value; + } + else + { + return Task.FromResult(AssertionResult.Failed("value was null")); + } + + var actualType = objectToCheck.GetType(); + + if (actualType.IsAssignableFrom(_sourceType)) + { + return AssertionResult._passedTask; + } + + return Task.FromResult(AssertionResult.Failed($"type {actualType.Name} is not assignable from {_sourceType.Name}")); + } + + protected override string GetExpectation() => $"to be assignable from {_sourceType.Name}"; +} + +/// +/// Asserts that a value's type is NOT assignable from a specific type. +/// Checks: !actualType.IsAssignableFrom(typeof(TSource)) +/// +public class IsNotAssignableFromAssertion : Assertion +{ + private readonly Type _sourceType; + + public IsNotAssignableFromAssertion( + AssertionContext context) + : base(context) + { + _sourceType = typeof(TSource); + } + + protected override Task CheckAsync(EvaluationMetadata metadata) + { + var value = metadata.Value; + var exception = metadata.Exception; + + object? objectToCheck = null; + + if (exception != null) + { + objectToCheck = exception; + } + else if (value != null) + { + objectToCheck = value; + } + else + { + return Task.FromResult(AssertionResult.Failed("value was null")); + } + + var actualType = objectToCheck.GetType(); + + if (!actualType.IsAssignableFrom(_sourceType)) + { + return AssertionResult._passedTask; + } + + return Task.FromResult(AssertionResult.Failed($"type {actualType.Name} is assignable from {_sourceType.Name}")); + } + + protected override string GetExpectation() => $"to not be assignable from {_sourceType.Name}"; +} + /// /// Asserts that a value is exactly of the specified type (using runtime Type parameter). /// diff --git a/TUnit.Assertions/Conditions/Wrappers/CountWrapper.cs b/TUnit.Assertions/Conditions/Wrappers/CountWrapper.cs index f2b2ea3364..70eb43d4e1 100644 --- a/TUnit.Assertions/Conditions/Wrappers/CountWrapper.cs +++ b/TUnit.Assertions/Conditions/Wrappers/CountWrapper.cs @@ -53,6 +53,26 @@ IsNotAssignableToAssertion IAssertionSource.I "Use: Assert.That(value).IsNotAssignableTo>().HasCount().EqualTo(5)"); } + /// + /// Not supported on CountWrapper - use IsAssignableFrom on the assertion source before calling HasCount(). + /// + IsAssignableFromAssertion IAssertionSource.IsAssignableFrom() + { + throw new NotSupportedException( + "IsAssignableFrom is not supported after HasCount(). " + + "Use: Assert.That(value).IsAssignableFrom>().HasCount().EqualTo(5)"); + } + + /// + /// Not supported on CountWrapper - use IsNotAssignableFrom on the assertion source before calling HasCount(). + /// + IsNotAssignableFromAssertion IAssertionSource.IsNotAssignableFrom() + { + throw new NotSupportedException( + "IsNotAssignableFrom is not supported after HasCount(). " + + "Use: Assert.That(value).IsNotAssignableFrom>().HasCount().EqualTo(5)"); + } + /// /// Not supported on CountWrapper - use IsNotTypeOf on the assertion source before calling HasCount(). /// diff --git a/TUnit.Assertions/Conditions/Wrappers/LengthWrapper.cs b/TUnit.Assertions/Conditions/Wrappers/LengthWrapper.cs index c1d027bfcf..4518e6068f 100644 --- a/TUnit.Assertions/Conditions/Wrappers/LengthWrapper.cs +++ b/TUnit.Assertions/Conditions/Wrappers/LengthWrapper.cs @@ -50,6 +50,26 @@ IsNotAssignableToAssertion IAssertionSource.IsNotAssign "Use: Assert.That(value).IsNotAssignableTo().HasLength().EqualTo(5)"); } + /// + /// Not supported on LengthWrapper - use IsAssignableFrom on the assertion source before calling HasLength(). + /// + IsAssignableFromAssertion IAssertionSource.IsAssignableFrom() + { + throw new NotSupportedException( + "IsAssignableFrom is not supported after HasLength(). " + + "Use: Assert.That(value).IsAssignableFrom().HasLength().EqualTo(5)"); + } + + /// + /// Not supported on LengthWrapper - use IsNotAssignableFrom on the assertion source before calling HasLength(). + /// + IsNotAssignableFromAssertion IAssertionSource.IsNotAssignableFrom() + { + throw new NotSupportedException( + "IsNotAssignableFrom is not supported after HasLength(). " + + "Use: Assert.That(value).IsNotAssignableFrom().HasLength().EqualTo(5)"); + } + /// /// Not supported on LengthWrapper - use IsNotTypeOf on the assertion source before calling HasLength(). /// diff --git a/TUnit.Assertions/Core/IAssertionSource.cs b/TUnit.Assertions/Core/IAssertionSource.cs index 92a34e915a..719adddad7 100644 --- a/TUnit.Assertions/Core/IAssertionSource.cs +++ b/TUnit.Assertions/Core/IAssertionSource.cs @@ -42,4 +42,14 @@ public interface IAssertionSource : IAssertionSource /// Asserts that the value's type is NOT assignable to the specified type. /// IsNotAssignableToAssertion IsNotAssignableTo(); + + /// + /// Asserts that the value's type is assignable from the specified type. + /// + IsAssignableFromAssertion IsAssignableFrom(); + + /// + /// Asserts that the value's type is NOT assignable from the specified type. + /// + IsNotAssignableFromAssertion IsNotAssignableFrom(); } diff --git a/TUnit.Assertions/Sources/AsyncDelegateAssertion.cs b/TUnit.Assertions/Sources/AsyncDelegateAssertion.cs index 0d2ea6c352..316747fa7d 100644 --- a/TUnit.Assertions/Sources/AsyncDelegateAssertion.cs +++ b/TUnit.Assertions/Sources/AsyncDelegateAssertion.cs @@ -153,6 +153,18 @@ TypeOfAssertion IAssertionSource.IsTypeOf() return new IsNotAssignableToAssertion(Context); } + public IsAssignableFromAssertion IsAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TSource).Name}>()"); + return new IsAssignableFromAssertion(Context); + } + + public IsNotAssignableFromAssertion IsNotAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TSource).Name}>()"); + return new IsNotAssignableFromAssertion(Context); + } + /// /// Explicit interface implementation for Task assignability checking. /// Asserts that the task itself is assignable to the specified type. @@ -173,6 +185,26 @@ IsNotAssignableToAssertion IAssertionSource.IsNotAssignable return new IsNotAssignableToAssertion(TaskContext); } + /// + /// Explicit interface implementation for Task assignability checking. + /// Asserts that the task itself is assignable from the specified type. + /// + IsAssignableFromAssertion IAssertionSource.IsAssignableFrom() + { + TaskContext.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TSource).Name}>()"); + return new IsAssignableFromAssertion(TaskContext); + } + + /// + /// Explicit interface implementation for Task assignability checking. + /// Asserts that the task itself is not assignable from the specified type. + /// + IsNotAssignableFromAssertion IAssertionSource.IsNotAssignableFrom() + { + TaskContext.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TSource).Name}>()"); + return new IsNotAssignableFromAssertion(TaskContext); + } + /// /// Asserts that the value is NOT of the specified type. /// Example: await Assert.That(async () => await SomeMethodAsync()).IsNotTypeOf(); diff --git a/TUnit.Assertions/Sources/AsyncEnumerableAssertionBase.cs b/TUnit.Assertions/Sources/AsyncEnumerableAssertionBase.cs index bf74e8792c..7ae256b726 100644 --- a/TUnit.Assertions/Sources/AsyncEnumerableAssertionBase.cs +++ b/TUnit.Assertions/Sources/AsyncEnumerableAssertionBase.cs @@ -211,6 +211,20 @@ public IsNotAssignableToAssertion> IsNotAssig return new IsNotAssignableToAssertion>(Context); } + /// + public IsAssignableFromAssertion> IsAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TExpected).Name}>()"); + return new IsAssignableFromAssertion>(Context); + } + + /// + public IsNotAssignableFromAssertion> IsNotAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TExpected).Name}>()"); + return new IsNotAssignableFromAssertion>(Context); + } + // CheckAsync is not overridden here - it's abstract in Assertion // Each concrete assertion class will implement it } diff --git a/TUnit.Assertions/Sources/AsyncFuncAssertion.cs b/TUnit.Assertions/Sources/AsyncFuncAssertion.cs index 0c4484b04f..a34a49f244 100644 --- a/TUnit.Assertions/Sources/AsyncFuncAssertion.cs +++ b/TUnit.Assertions/Sources/AsyncFuncAssertion.cs @@ -55,6 +55,18 @@ public IsNotAssignableToAssertion IsNotAssignableTo() return new IsNotAssignableToAssertion(Context); } + public IsAssignableFromAssertion IsAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TSource).Name}>()"); + return new IsAssignableFromAssertion(Context); + } + + public IsNotAssignableFromAssertion IsNotAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TSource).Name}>()"); + return new IsNotAssignableFromAssertion(Context); + } + /// /// Asserts that the async function result is NOT of the specified type. /// Example: await Assert.That(async () => await GetValueAsync()).IsNotTypeOf(); diff --git a/TUnit.Assertions/Sources/CollectionAssertionBase.cs b/TUnit.Assertions/Sources/CollectionAssertionBase.cs index 19cc07bb9e..31e84310f4 100644 --- a/TUnit.Assertions/Sources/CollectionAssertionBase.cs +++ b/TUnit.Assertions/Sources/CollectionAssertionBase.cs @@ -67,6 +67,18 @@ public IsNotAssignableToAssertion IsNotAssignableTo(Context); } + public IsAssignableFromAssertion IsAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TSource).Name}>()"); + return new IsAssignableFromAssertion(Context); + } + + public IsNotAssignableFromAssertion IsNotAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TSource).Name}>()"); + return new IsNotAssignableFromAssertion(Context); + } + /// /// Asserts that the collection is NOT of the specified type. /// This allows chaining additional assertions on the value. diff --git a/TUnit.Assertions/Sources/DelegateAssertion.cs b/TUnit.Assertions/Sources/DelegateAssertion.cs index 142aa6e0ee..faf7dc3b46 100644 --- a/TUnit.Assertions/Sources/DelegateAssertion.cs +++ b/TUnit.Assertions/Sources/DelegateAssertion.cs @@ -58,6 +58,18 @@ public DelegateAssertion(Action action, string? expression) return new IsNotAssignableToAssertion(Context); } + public IsAssignableFromAssertion IsAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TSource).Name}>()"); + return new IsAssignableFromAssertion(Context); + } + + public IsNotAssignableFromAssertion IsNotAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TSource).Name}>()"); + return new IsNotAssignableFromAssertion(Context); + } + /// /// Asserts that the value is NOT of the specified type. /// Example: await Assert.That(() => SomeMethod()).IsNotTypeOf(); diff --git a/TUnit.Assertions/Sources/FuncAssertion.cs b/TUnit.Assertions/Sources/FuncAssertion.cs index bcdf9a4450..b57e4ddb6d 100644 --- a/TUnit.Assertions/Sources/FuncAssertion.cs +++ b/TUnit.Assertions/Sources/FuncAssertion.cs @@ -55,6 +55,18 @@ public IsNotAssignableToAssertion IsNotAssignableTo() return new IsNotAssignableToAssertion(Context); } + public IsAssignableFromAssertion IsAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TSource).Name}>()"); + return new IsAssignableFromAssertion(Context); + } + + public IsNotAssignableFromAssertion IsNotAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TSource).Name}>()"); + return new IsNotAssignableFromAssertion(Context); + } + /// /// Asserts that the value is NOT of the specified type. /// Example: await Assert.That(() => GetValue()).IsNotTypeOf(); diff --git a/TUnit.Assertions/Sources/MemoryAssertionBase.cs b/TUnit.Assertions/Sources/MemoryAssertionBase.cs index d80f5ba8d9..deadf3bc32 100644 --- a/TUnit.Assertions/Sources/MemoryAssertionBase.cs +++ b/TUnit.Assertions/Sources/MemoryAssertionBase.cs @@ -67,6 +67,18 @@ public IsNotAssignableToAssertion IsNotAssignableTo() return new IsNotAssignableToAssertion(Context); } + public IsAssignableFromAssertion IsAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TSource).Name}>()"); + return new IsAssignableFromAssertion(Context); + } + + public IsNotAssignableFromAssertion IsNotAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TSource).Name}>()"); + return new IsNotAssignableFromAssertion(Context); + } + public IsNotTypeOfAssertion IsNotTypeOf() { Context.ExpressionBuilder.Append($".IsNotTypeOf<{typeof(TExpected).Name}>()"); diff --git a/TUnit.Assertions/Sources/TaskAssertion.cs b/TUnit.Assertions/Sources/TaskAssertion.cs index bfc7504263..17df49520e 100644 --- a/TUnit.Assertions/Sources/TaskAssertion.cs +++ b/TUnit.Assertions/Sources/TaskAssertion.cs @@ -85,6 +85,18 @@ public IsNotAssignableToAssertion IsNotAssignableTo() return new IsNotAssignableToAssertion(Context); } + public IsAssignableFromAssertion IsAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TSource).Name}>()"); + return new IsAssignableFromAssertion(Context); + } + + public IsNotAssignableFromAssertion IsNotAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TSource).Name}>()"); + return new IsNotAssignableFromAssertion(Context); + } + /// /// Explicit interface implementation for Task<TValue?> assignability checking. /// Asserts that the task itself is assignable to the specified type. @@ -105,6 +117,26 @@ public IsNotAssignableToAssertion IsNotAssignableTo() return new IsNotAssignableToAssertion>(TaskContext); } + /// + /// Explicit interface implementation for Task<TValue?> assignability checking. + /// Asserts that the task itself is assignable from the specified type. + /// + IsAssignableFromAssertion> IAssertionSource>.IsAssignableFrom() + { + TaskContext.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TSource).Name}>()"); + return new IsAssignableFromAssertion>(TaskContext); + } + + /// + /// Explicit interface implementation for Task<TValue?> assignability checking. + /// Asserts that the task itself is not assignable from the specified type. + /// + IsNotAssignableFromAssertion> IAssertionSource>.IsNotAssignableFrom() + { + TaskContext.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TSource).Name}>()"); + return new IsNotAssignableFromAssertion>(TaskContext); + } + /// /// Asserts that the task result is NOT of the specified type. /// Example: await Assert.That(GetValueAsync()).IsNotTypeOf(); diff --git a/TUnit.Assertions/Sources/ValueAssertion.cs b/TUnit.Assertions/Sources/ValueAssertion.cs index b6ce07b451..ce43968519 100644 --- a/TUnit.Assertions/Sources/ValueAssertion.cs +++ b/TUnit.Assertions/Sources/ValueAssertion.cs @@ -78,6 +78,18 @@ public IsNotAssignableToAssertion IsNotAssignableTo() return new IsNotAssignableToAssertion(Context); } + public IsAssignableFromAssertion IsAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TSource).Name}>()"); + return new IsAssignableFromAssertion(Context); + } + + public IsNotAssignableFromAssertion IsNotAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TSource).Name}>()"); + return new IsNotAssignableFromAssertion(Context); + } + /// /// Asserts that the value is NOT of the specified type. /// This instance method allows single type parameter usage without needing to specify the source type. diff --git a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt index c077811935..cd15c6e5f7 100644 --- a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt +++ b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt @@ -337,7 +337,9 @@ namespace .Assertions { public . Context { get; } public . GetAwaiter() { } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -409,7 +411,9 @@ namespace . public WhenParsedIntoAssertion(. stringContext, ? formatProvider = null) { } protected override .<.> CheckAsync(. metadata) { } protected override string GetExpectation() { } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -612,7 +616,9 @@ namespace .Conditions { public AssertionSourceAdapter(. context) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -1341,6 +1347,12 @@ namespace .Conditions protected override string GetExpectation() { } public . Within(int tolerance) { } } + public class IsAssignableFromAssertion : . + { + public IsAssignableFromAssertion(. context) { } + protected override .<.> CheckAsync(. metadata) { } + protected override string GetExpectation() { } + } public class IsAssignableToAssertion : . { public IsAssignableToAssertion(. context) { } @@ -1393,6 +1405,12 @@ namespace .Conditions public . Using(. comparer) { } public . Using( equalityPredicate) { } } + public class IsNotAssignableFromAssertion : . + { + public IsNotAssignableFromAssertion(. context) { } + protected override .<.> CheckAsync(. metadata) { } + protected override string GetExpectation() { } + } public class IsNotAssignableToAssertion : . { public IsNotAssignableToAssertion(. context) { } @@ -1484,8 +1502,10 @@ namespace .Conditions { public ListItemAtSource(. listContext, int index) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEqualTo(TItem expected, [.("expected")] string? expression = null) { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEqualTo(TItem expected, [.("expected")] string? expression = null) { } public . IsNotNull() { } @@ -1520,8 +1540,10 @@ namespace .Conditions { public ListLastItemSource(. listContext) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEqualTo(TItem expected, [.("expected")] string? expression = null) { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEqualTo(TItem expected, [.("expected")] string? expression = null) { } public . IsNotNull() { } @@ -1731,8 +1753,10 @@ namespace .Conditions { public ReadOnlyListItemAtSource(. listContext, int index) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEqualTo(TItem expected, [.("expected")] string? expression = null) { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEqualTo(TItem expected, [.("expected")] string? expression = null) { } public . IsNotNull() { } @@ -1767,8 +1791,10 @@ namespace .Conditions { public ReadOnlyListLastItemSource(. listContext) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEqualTo(TItem expected, [.("expected")] string? expression = null) { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEqualTo(TItem expected, [.("expected")] string? expression = null) { } public . IsNotNull() { } @@ -2429,7 +2455,9 @@ namespace .Core public interface IAssertionSource : . { . Context { get; } + . IsAssignableFrom(); . IsAssignableTo(); + . IsNotAssignableFrom(); . IsNotAssignableTo(); . IsNotTypeOf(); . IsTypeOf(); @@ -5667,11 +5695,13 @@ namespace .Sources { public AsyncDelegateAssertion(<.> action, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public .<.> IsCanceled() { } public .<.> IsCompleted() { } public .<.> IsCompletedSuccessfully() { } public .<.> IsFaulted() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public .<.> IsNotCanceled() { } public .<.> IsNotCompleted() { } @@ -5702,8 +5732,10 @@ namespace .Sources public . HasAtMost(int maxCount, [.("maxCount")] string? expression = null) { } public . HasCount(int expected, [.("expected")] string? expression = null) { } public . HasCountBetween(int min, int max, [.("min")] string? minExpression = null, [.("max")] string? maxExpression = null) { } + public .> IsAssignableFrom() { } public .> IsAssignableTo() { } public . IsEmpty() { } + public .> IsNotAssignableFrom() { } public .> IsNotAssignableTo() { } public . IsNotEmpty() { } public .<., TExpected> IsNotTypeOf() { } @@ -5717,7 +5749,9 @@ namespace .Sources { public AsyncFuncAssertion(<.> func, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -5767,10 +5801,12 @@ namespace .Sources public . HasDistinctItems(. comparer, [.("comparer")] string? comparerExpression = null) { } public . HasSingleItem() { } public . HasSingleItem( predicate, [.("predicate")] string? expression = null) { } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEmpty() { } public . IsInDescendingOrder() { } public . IsInOrder() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEmpty() { } public . IsNotTypeOf() { } @@ -5788,7 +5824,9 @@ namespace .Sources { public DelegateAssertion( action, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -5825,7 +5863,9 @@ namespace .Sources { public FuncAssertion( func, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -5882,10 +5922,12 @@ namespace .Sources protected override string GetExpectation() { } public . HasDistinctItems() { } public . HasSingleItem() { } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEmpty() { } public . IsInDescendingOrder() { } public . IsInOrder() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEmpty() { } public . IsNotTypeOf() { } @@ -5984,11 +6026,13 @@ namespace .Sources { public TaskAssertion(. task, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public .<.> IsCanceled() { } public .<.> IsCompleted() { } public .<.> IsCompletedSuccessfully() { } public .<.> IsFaulted() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public .<.> IsNotCanceled() { } public .<.> IsNotCompleted() { } @@ -6006,7 +6050,9 @@ namespace .Sources protected ValueAssertion(. context) { } public ValueAssertion(TValue? value, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } diff --git a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt index 22c60eeb60..983d006d97 100644 --- a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt +++ b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt @@ -320,7 +320,9 @@ namespace .Assertions { public . Context { get; } public . GetAwaiter() { } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -392,7 +394,9 @@ namespace . public WhenParsedIntoAssertion(. stringContext, ? formatProvider = null) { } protected override .<.> CheckAsync(. metadata) { } protected override string GetExpectation() { } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -595,7 +599,9 @@ namespace .Conditions { public AssertionSourceAdapter(. context) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -1324,6 +1330,12 @@ namespace .Conditions protected override string GetExpectation() { } public . Within(int tolerance) { } } + public class IsAssignableFromAssertion : . + { + public IsAssignableFromAssertion(. context) { } + protected override .<.> CheckAsync(. metadata) { } + protected override string GetExpectation() { } + } public class IsAssignableToAssertion : . { public IsAssignableToAssertion(. context) { } @@ -1376,6 +1388,12 @@ namespace .Conditions public . Using(. comparer) { } public . Using( equalityPredicate) { } } + public class IsNotAssignableFromAssertion : . + { + public IsNotAssignableFromAssertion(. context) { } + protected override .<.> CheckAsync(. metadata) { } + protected override string GetExpectation() { } + } public class IsNotAssignableToAssertion : . { public IsNotAssignableToAssertion(. context) { } @@ -1467,8 +1485,10 @@ namespace .Conditions { public ListItemAtSource(. listContext, int index) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEqualTo(TItem expected, [.("expected")] string? expression = null) { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEqualTo(TItem expected, [.("expected")] string? expression = null) { } public . IsNotNull() { } @@ -1503,8 +1523,10 @@ namespace .Conditions { public ListLastItemSource(. listContext) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEqualTo(TItem expected, [.("expected")] string? expression = null) { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEqualTo(TItem expected, [.("expected")] string? expression = null) { } public . IsNotNull() { } @@ -1714,8 +1736,10 @@ namespace .Conditions { public ReadOnlyListItemAtSource(. listContext, int index) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEqualTo(TItem expected, [.("expected")] string? expression = null) { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEqualTo(TItem expected, [.("expected")] string? expression = null) { } public . IsNotNull() { } @@ -1750,8 +1774,10 @@ namespace .Conditions { public ReadOnlyListLastItemSource(. listContext) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEqualTo(TItem expected, [.("expected")] string? expression = null) { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEqualTo(TItem expected, [.("expected")] string? expression = null) { } public . IsNotNull() { } @@ -2408,7 +2434,9 @@ namespace .Core public interface IAssertionSource : . { . Context { get; } + . IsAssignableFrom(); . IsAssignableTo(); + . IsNotAssignableFrom(); . IsNotAssignableTo(); . IsNotTypeOf(); . IsTypeOf(); @@ -5615,11 +5643,13 @@ namespace .Sources { public AsyncDelegateAssertion(<.> action, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public .<.> IsCanceled() { } public .<.> IsCompleted() { } public .<.> IsCompletedSuccessfully() { } public .<.> IsFaulted() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public .<.> IsNotCanceled() { } public .<.> IsNotCompleted() { } @@ -5650,8 +5680,10 @@ namespace .Sources public . HasAtMost(int maxCount, [.("maxCount")] string? expression = null) { } public . HasCount(int expected, [.("expected")] string? expression = null) { } public . HasCountBetween(int min, int max, [.("min")] string? minExpression = null, [.("max")] string? maxExpression = null) { } + public .> IsAssignableFrom() { } public .> IsAssignableTo() { } public . IsEmpty() { } + public .> IsNotAssignableFrom() { } public .> IsNotAssignableTo() { } public . IsNotEmpty() { } public .<., TExpected> IsNotTypeOf() { } @@ -5665,7 +5697,9 @@ namespace .Sources { public AsyncFuncAssertion(<.> func, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -5714,10 +5748,12 @@ namespace .Sources public . HasDistinctItems(. comparer, [.("comparer")] string? comparerExpression = null) { } public . HasSingleItem() { } public . HasSingleItem( predicate, [.("predicate")] string? expression = null) { } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEmpty() { } public . IsInDescendingOrder() { } public . IsInOrder() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEmpty() { } public . IsNotTypeOf() { } @@ -5735,7 +5771,9 @@ namespace .Sources { public DelegateAssertion( action, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -5772,7 +5810,9 @@ namespace .Sources { public FuncAssertion( func, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -5829,10 +5869,12 @@ namespace .Sources protected override string GetExpectation() { } public . HasDistinctItems() { } public . HasSingleItem() { } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEmpty() { } public . IsInDescendingOrder() { } public . IsInOrder() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEmpty() { } public . IsNotTypeOf() { } @@ -5931,11 +5973,13 @@ namespace .Sources { public TaskAssertion(. task, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public .<.> IsCanceled() { } public .<.> IsCompleted() { } public .<.> IsCompletedSuccessfully() { } public .<.> IsFaulted() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public .<.> IsNotCanceled() { } public .<.> IsNotCompleted() { } @@ -5953,7 +5997,9 @@ namespace .Sources protected ValueAssertion(. context) { } public ValueAssertion(TValue? value, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } diff --git a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt index 2444ba3104..73b0f8555f 100644 --- a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt +++ b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt @@ -337,7 +337,9 @@ namespace .Assertions { public . Context { get; } public . GetAwaiter() { } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -409,7 +411,9 @@ namespace . public WhenParsedIntoAssertion(. stringContext, ? formatProvider = null) { } protected override .<.> CheckAsync(. metadata) { } protected override string GetExpectation() { } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -612,7 +616,9 @@ namespace .Conditions { public AssertionSourceAdapter(. context) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -1341,6 +1347,12 @@ namespace .Conditions protected override string GetExpectation() { } public . Within(int tolerance) { } } + public class IsAssignableFromAssertion : . + { + public IsAssignableFromAssertion(. context) { } + protected override .<.> CheckAsync(. metadata) { } + protected override string GetExpectation() { } + } public class IsAssignableToAssertion : . { public IsAssignableToAssertion(. context) { } @@ -1393,6 +1405,12 @@ namespace .Conditions public . Using(. comparer) { } public . Using( equalityPredicate) { } } + public class IsNotAssignableFromAssertion : . + { + public IsNotAssignableFromAssertion(. context) { } + protected override .<.> CheckAsync(. metadata) { } + protected override string GetExpectation() { } + } public class IsNotAssignableToAssertion : . { public IsNotAssignableToAssertion(. context) { } @@ -1484,8 +1502,10 @@ namespace .Conditions { public ListItemAtSource(. listContext, int index) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEqualTo(TItem expected, [.("expected")] string? expression = null) { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEqualTo(TItem expected, [.("expected")] string? expression = null) { } public . IsNotNull() { } @@ -1520,8 +1540,10 @@ namespace .Conditions { public ListLastItemSource(. listContext) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEqualTo(TItem expected, [.("expected")] string? expression = null) { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEqualTo(TItem expected, [.("expected")] string? expression = null) { } public . IsNotNull() { } @@ -1731,8 +1753,10 @@ namespace .Conditions { public ReadOnlyListItemAtSource(. listContext, int index) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEqualTo(TItem expected, [.("expected")] string? expression = null) { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEqualTo(TItem expected, [.("expected")] string? expression = null) { } public . IsNotNull() { } @@ -1767,8 +1791,10 @@ namespace .Conditions { public ReadOnlyListLastItemSource(. listContext) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEqualTo(TItem expected, [.("expected")] string? expression = null) { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEqualTo(TItem expected, [.("expected")] string? expression = null) { } public . IsNotNull() { } @@ -2429,7 +2455,9 @@ namespace .Core public interface IAssertionSource : . { . Context { get; } + . IsAssignableFrom(); . IsAssignableTo(); + . IsNotAssignableFrom(); . IsNotAssignableTo(); . IsNotTypeOf(); . IsTypeOf(); @@ -5667,11 +5695,13 @@ namespace .Sources { public AsyncDelegateAssertion(<.> action, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public .<.> IsCanceled() { } public .<.> IsCompleted() { } public .<.> IsCompletedSuccessfully() { } public .<.> IsFaulted() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public .<.> IsNotCanceled() { } public .<.> IsNotCompleted() { } @@ -5702,8 +5732,10 @@ namespace .Sources public . HasAtMost(int maxCount, [.("maxCount")] string? expression = null) { } public . HasCount(int expected, [.("expected")] string? expression = null) { } public . HasCountBetween(int min, int max, [.("min")] string? minExpression = null, [.("max")] string? maxExpression = null) { } + public .> IsAssignableFrom() { } public .> IsAssignableTo() { } public . IsEmpty() { } + public .> IsNotAssignableFrom() { } public .> IsNotAssignableTo() { } public . IsNotEmpty() { } public .<., TExpected> IsNotTypeOf() { } @@ -5717,7 +5749,9 @@ namespace .Sources { public AsyncFuncAssertion(<.> func, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -5767,10 +5801,12 @@ namespace .Sources public . HasDistinctItems(. comparer, [.("comparer")] string? comparerExpression = null) { } public . HasSingleItem() { } public . HasSingleItem( predicate, [.("predicate")] string? expression = null) { } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEmpty() { } public . IsInDescendingOrder() { } public . IsInOrder() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEmpty() { } public . IsNotTypeOf() { } @@ -5788,7 +5824,9 @@ namespace .Sources { public DelegateAssertion( action, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -5825,7 +5863,9 @@ namespace .Sources { public FuncAssertion( func, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -5882,10 +5922,12 @@ namespace .Sources protected override string GetExpectation() { } public . HasDistinctItems() { } public . HasSingleItem() { } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEmpty() { } public . IsInDescendingOrder() { } public . IsInOrder() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEmpty() { } public . IsNotTypeOf() { } @@ -5984,11 +6026,13 @@ namespace .Sources { public TaskAssertion(. task, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public .<.> IsCanceled() { } public .<.> IsCompleted() { } public .<.> IsCompletedSuccessfully() { } public .<.> IsFaulted() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public .<.> IsNotCanceled() { } public .<.> IsNotCompleted() { } @@ -6006,7 +6050,9 @@ namespace .Sources protected ValueAssertion(. context) { } public ValueAssertion(TValue? value, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } diff --git a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt index 3457173c39..1f3f9021de 100644 --- a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt +++ b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt @@ -279,7 +279,9 @@ namespace .Assertions { public . Context { get; } public . GetAwaiter() { } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -351,7 +353,9 @@ namespace . public WhenParsedIntoAssertion(. stringContext, ? formatProvider = null) { } protected override .<.> CheckAsync(. metadata) { } protected override string GetExpectation() { } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -466,7 +470,9 @@ namespace .Conditions { public AssertionSourceAdapter(. context) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -1170,6 +1176,12 @@ namespace .Conditions protected override string GetExpectation() { } public . Within(int tolerance) { } } + public class IsAssignableFromAssertion : . + { + public IsAssignableFromAssertion(. context) { } + protected override .<.> CheckAsync(. metadata) { } + protected override string GetExpectation() { } + } public class IsAssignableToAssertion : . { public IsAssignableToAssertion(. context) { } @@ -1220,6 +1232,12 @@ namespace .Conditions public . Using(. comparer) { } public . Using( equalityPredicate) { } } + public class IsNotAssignableFromAssertion : . + { + public IsNotAssignableFromAssertion(. context) { } + protected override .<.> CheckAsync(. metadata) { } + protected override string GetExpectation() { } + } public class IsNotAssignableToAssertion : . { public IsNotAssignableToAssertion(. context) { } @@ -1311,8 +1329,10 @@ namespace .Conditions { public ListItemAtSource(. listContext, int index) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEqualTo(TItem expected, [.("expected")] string? expression = null) { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEqualTo(TItem expected, [.("expected")] string? expression = null) { } public . IsNotNull() { } @@ -1347,8 +1367,10 @@ namespace .Conditions { public ListLastItemSource(. listContext) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEqualTo(TItem expected, [.("expected")] string? expression = null) { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEqualTo(TItem expected, [.("expected")] string? expression = null) { } public . IsNotNull() { } @@ -1555,8 +1577,10 @@ namespace .Conditions { public ReadOnlyListItemAtSource(. listContext, int index) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEqualTo(TItem expected, [.("expected")] string? expression = null) { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEqualTo(TItem expected, [.("expected")] string? expression = null) { } public . IsNotNull() { } @@ -1591,8 +1615,10 @@ namespace .Conditions { public ReadOnlyListLastItemSource(. listContext) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEqualTo(TItem expected, [.("expected")] string? expression = null) { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEqualTo(TItem expected, [.("expected")] string? expression = null) { } public . IsNotNull() { } @@ -2190,7 +2216,9 @@ namespace .Core public interface IAssertionSource : . { . Context { get; } + . IsAssignableFrom(); . IsAssignableTo(); + . IsNotAssignableFrom(); . IsNotAssignableTo(); . IsNotTypeOf(); . IsTypeOf(); @@ -4955,10 +4983,12 @@ namespace .Sources { public AsyncDelegateAssertion(<.> action, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public .<.> IsCanceled() { } public .<.> IsCompleted() { } public .<.> IsFaulted() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public .<.> IsNotCanceled() { } public .<.> IsNotCompleted() { } @@ -4988,8 +5018,10 @@ namespace .Sources public . HasAtMost(int maxCount, [.("maxCount")] string? expression = null) { } public . HasCount(int expected, [.("expected")] string? expression = null) { } public . HasCountBetween(int min, int max, [.("min")] string? minExpression = null, [.("max")] string? maxExpression = null) { } + public .> IsAssignableFrom() { } public .> IsAssignableTo() { } public . IsEmpty() { } + public .> IsNotAssignableFrom() { } public .> IsNotAssignableTo() { } public . IsNotEmpty() { } public .<., TExpected> IsNotTypeOf() { } @@ -5003,7 +5035,9 @@ namespace .Sources { public AsyncFuncAssertion(<.> func, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -5052,10 +5086,12 @@ namespace .Sources public . HasDistinctItems(. comparer, [.("comparer")] string? comparerExpression = null) { } public . HasSingleItem() { } public . HasSingleItem( predicate, [.("predicate")] string? expression = null) { } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public . IsEmpty() { } public . IsInDescendingOrder() { } public . IsInOrder() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotEmpty() { } public . IsNotTypeOf() { } @@ -5073,7 +5109,9 @@ namespace .Sources { public DelegateAssertion( action, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -5110,7 +5148,9 @@ namespace .Sources { public FuncAssertion( func, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } @@ -5221,10 +5261,12 @@ namespace .Sources { public TaskAssertion(. task, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } public .<.> IsCanceled() { } public .<.> IsCompleted() { } public .<.> IsFaulted() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public .<.> IsNotCanceled() { } public .<.> IsNotCompleted() { } @@ -5241,7 +5283,9 @@ namespace .Sources protected ValueAssertion(. context) { } public ValueAssertion(TValue? value, string? expression) { } public . Context { get; } + public . IsAssignableFrom() { } public . IsAssignableTo() { } + public . IsNotAssignableFrom() { } public . IsNotAssignableTo() { } public . IsNotTypeOf() { } public . IsTypeOf() { } diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Playwright._.verified/TUnit.Playwright/TUnit.Playwright.csproj b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Playwright._.verified/TUnit.Playwright/TUnit.Playwright.csproj index 72195c7b1d..f38c961198 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Playwright._.verified/TUnit.Playwright/TUnit.Playwright.csproj +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Playwright._.verified/TUnit.Playwright/TUnit.Playwright.csproj @@ -4,7 +4,7 @@ enable enable Exe - net8.0 + net10.0 diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/BasicTests.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/BasicTests.cs new file mode 100644 index 0000000000..2d9b94b135 --- /dev/null +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/BasicTests.cs @@ -0,0 +1,53 @@ +namespace TUnit; + +public class BasicTests +{ + [Before(Class)] + public static Task BeforeClass(ClassHookContext context) + { + // Runs once before all tests in this class + return Task.CompletedTask; + } + + [After(Class)] + public static Task AfterClass(ClassHookContext context) + { + // Runs once after all tests in this class + return Task.CompletedTask; + } + + [Before(Test)] + public Task BeforeTest(TestContext context) + { + // Runs before each test in this class + return Task.CompletedTask; + } + + [After(Test)] + public Task AfterTest(TestContext context) + { + // Runs after each test in this class + return Task.CompletedTask; + } + + [Test] + public async Task Add_ReturnsSum() + { + var calculator = new Calculator(); + + var result = calculator.Add(1, 2); + + await Assert.That(result).IsEqualTo(3); + } + + [Test] + public async Task Divide_ByZero_ThrowsException() + { + var calculator = new Calculator(); + + var action = () => calculator.Divide(1, 0); + + await Assert.That(action).ThrowsException() + .WithMessage("Attempted to divide by zero."); + } +} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Calculator.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Calculator.cs new file mode 100644 index 0000000000..4b7541df77 --- /dev/null +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Calculator.cs @@ -0,0 +1,10 @@ +namespace TUnit; + +public class Calculator +{ + public int Add(int a, int b) => a + b; + public int Subtract(int a, int b) => a - b; + public int Multiply(int a, int b) => a * b; + public double Divide(int a, int b) => + b == 0 ? throw new DivideByZeroException() : (double)a / b; +} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Data/AdditionDataGenerator.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Data/AdditionDataGenerator.cs new file mode 100644 index 0000000000..834274f5dc --- /dev/null +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Data/AdditionDataGenerator.cs @@ -0,0 +1,12 @@ +namespace TUnit; + +public class AdditionDataGeneratorAttribute : DataSourceGeneratorAttribute +{ + protected override IEnumerable> GenerateDataSources( + DataGeneratorMetadata dataGeneratorMetadata) + { + yield return () => (1, 1, 2); + yield return () => (4, 5, 9); + yield return () => (-1, 1, 0); + } +} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Data/DataClass.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Data/DataClass.cs deleted file mode 100644 index 065b653e3c..0000000000 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Data/DataClass.cs +++ /dev/null @@ -1,16 +0,0 @@ -using TUnit.Core.Interfaces; - -namespace TUnit; - -public class DataClass : IAsyncInitializer, IAsyncDisposable -{ - public Task InitializeAsync() - { - return Console.Out.WriteLineAsync("Classes can be injected into tests, and they can perform some initialisation logic such as starting an in-memory server or a test container."); - } - - public async ValueTask DisposeAsync() - { - await Console.Out.WriteLineAsync("And when the class is finished with, we can clean up any resources."); - } -} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Data/DataSourceGenerator.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Data/DataSourceGenerator.cs deleted file mode 100644 index b08733deec..0000000000 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Data/DataSourceGenerator.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace TUnit.Data; - -public class DataGenerator : DataSourceGeneratorAttribute -{ - protected override IEnumerable> GenerateDataSources(DataGeneratorMetadata dataGeneratorMetadata) - { - yield return () => (1, 1, 2); - yield return () => (1, 2, 3); - yield return () => (4, 5, 9); - } -} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Data/DependencyInjectionClassConstructor.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Data/DependencyInjectionClassConstructor.cs deleted file mode 100644 index 24043e34f5..0000000000 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Data/DependencyInjectionClassConstructor.cs +++ /dev/null @@ -1,18 +0,0 @@ -using TUnit.Core.Interfaces; - -namespace TUnit; - -public class DependencyInjectionClassConstructor : IClassConstructor -{ - public Task Create(Type type, ClassConstructorMetadata classConstructorMetadata) - { - Console.WriteLine(@"You can also control how your test classes are new'd up, giving you lots of power and the ability to utilise tools such as dependency injection"); - - if (type == typeof(AndEvenMoreTests)) - { - return Task.FromResult(new AndEvenMoreTests(new DataClass())); - } - - throw new NotImplementedException(); - } -} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Data/InMemoryDb.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Data/InMemoryDb.cs new file mode 100644 index 0000000000..bfba6cebf6 --- /dev/null +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Data/InMemoryDb.cs @@ -0,0 +1,34 @@ +using TUnit.Core.Interfaces; + +namespace TUnit; + +public class InMemoryDb : IAsyncInitializer, IAsyncDisposable +{ + private Dictionary _store = null!; + + public Task InitializeAsync() + { + // Simulate async setup - e.g. connecting to a database or starting a container + _store = new Dictionary(); + return Task.CompletedTask; + } + + public Task SetAsync(string key, string value) + { + _store[key] = value; + return Task.CompletedTask; + } + + public Task GetAsync(string key) + { + _store.TryGetValue(key, out var value); + return Task.FromResult(value); + } + + public ValueTask DisposeAsync() + { + // Simulate async teardown - e.g. closing connections, removing containers + _store.Clear(); + return ValueTask.CompletedTask; + } +} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/DataDrivenTests.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/DataDrivenTests.cs new file mode 100644 index 0000000000..5dea577de1 --- /dev/null +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/DataDrivenTests.cs @@ -0,0 +1,77 @@ +namespace TUnit; + +public class DataDrivenTests +{ + [Test] + [Arguments(1, 2, 3)] + [Arguments(5, -3, 2)] + [Arguments(0, 0, 0)] + public async Task Add_WithArguments(int a, int b, int expected) + { + var calculator = new Calculator(); + + var result = calculator.Add(a, b); + + await Assert.That(result).IsEqualTo(expected); + } + + [Test] + [MethodDataSource(nameof(SubtractCases))] + public async Task Subtract_WithMethodDataSource(int a, int b, int expected) + { + var calculator = new Calculator(); + + var result = calculator.Subtract(a, b); + + await Assert.That(result).IsEqualTo(expected); + } + + [Test] + [AdditionDataGenerator] + public async Task Add_WithCustomDataGenerator(int a, int b, int expected) + { + var calculator = new Calculator(); + + var result = calculator.Add(a, b); + + await Assert.That(result).IsEqualTo(expected); + } + + [Test] + [MatrixDataSource] + public async Task Multiply_AllCombinations( + [Matrix(1, 2, 3)] int a, + [Matrix(0, 1, -1)] int b) + { + var calculator = new Calculator(); + + var result = calculator.Multiply(a, b); + + await Assert.That(result).IsEqualTo(a * b); + } + + public static IEnumerable<(int, int, int)> SubtractCases() + { + yield return (5, 3, 2); + yield return (10, 7, 3); + yield return (0, 0, 0); + } +} + +// Arguments can also be applied at the class level to parameterize the constructor +[Arguments(10)] +[Arguments(100)] +public class ClassLevelArgumentTests(int divisor) +{ + [Test] + [Arguments(100)] + [Arguments(50)] + public async Task Divide_WithClassAndMethodArguments(int dividend) + { + var calculator = new Calculator(); + + var result = calculator.Divide(dividend, divisor); + + await Assert.That(result).IsGreaterThan(0); + } +} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/DependencyInjectionTests.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/DependencyInjectionTests.cs new file mode 100644 index 0000000000..b98a78b0de --- /dev/null +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/DependencyInjectionTests.cs @@ -0,0 +1,47 @@ +namespace TUnit; + +public class DependencyInjectionTests +{ + // A new InMemoryDb is created for each test + [Test] + [ClassDataSource] + public async Task Database_SetAndGet(InMemoryDb db) + { + await db.SetAsync("key", "value"); + + var result = await db.GetAsync("key"); + + await Assert.That(result).IsEqualTo("value"); + } + + // The same InMemoryDb instance is shared across all tests in this class + [Test] + [ClassDataSource(Shared = SharedType.PerClass)] + public async Task Database_SharedPerClass(InMemoryDb db) + { + await db.SetAsync("shared", "data"); + + var result = await db.GetAsync("shared"); + + await Assert.That(result).IsNotNull(); + } +} + +// ClassDataSource can also be applied at the class level +[ClassDataSource(Shared = SharedType.PerTestSession)] +public class SharedDatabaseTests(InMemoryDb db) +{ + // Or injected as a property instead of a constructor parameter + [ClassDataSource] + public required Calculator Calculator { get; init; } + + [Test] + public async Task Calculator_ResultCanBeStored() + { + var result = Calculator.Add(2, 3); + + await db.SetAsync("result", result.ToString()); + + await Assert.That(await db.GetAsync("result")).IsEqualTo("5"); + } +} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/GlobalSetup.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/GlobalSetup.cs deleted file mode 100644 index 7c7844d5c2..0000000000 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/GlobalSetup.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Here you could define global logic that would affect all tests - -// You can use attributes at the assembly level to apply to all tests in the assembly -[assembly: Retry(3)] -[assembly: System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - -namespace TUnit; - -public class GlobalHooks -{ - [Before(TestSession)] - public static void SetUp() - { - Console.WriteLine(@"Or you can define methods that do stuff before..."); - } - - [After(TestSession)] - public static void CleanUp() - { - Console.WriteLine(@"...and after!"); - } -} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/HooksAndLifecycle.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/HooksAndLifecycle.cs new file mode 100644 index 0000000000..64447d5af6 --- /dev/null +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/HooksAndLifecycle.cs @@ -0,0 +1,23 @@ +using System.Diagnostics.CodeAnalysis; + +[assembly: ExcludeFromCodeCoverage] + +namespace TUnit; + +public class GlobalHooks +{ + [Before(TestSession)] + public static Task BeforeTestSession(TestSessionContext context) + { + // Runs once before all tests - e.g. start a test container, seed a database + return Task.CompletedTask; + } + + [After(TestSession)] + public static Task AfterTestSession(TestSessionContext context) + { + // Runs once after all tests - e.g. stop containers, clean up resources + return Task.CompletedTask; + } + +} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/TUnit.csproj b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/TUnit.csproj index 784b713a3c..cd7e07380b 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/TUnit.csproj +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/TUnit.csproj @@ -1,14 +1,14 @@ - + enable enable Exe - net8.0 + net10.0 - \ No newline at end of file + diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Tests.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Tests.cs deleted file mode 100644 index ac6194e1e2..0000000000 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Tests.cs +++ /dev/null @@ -1,65 +0,0 @@ -using TUnit.Data; - -namespace TUnit; - -public class Tests -{ - [Test] - public void Basic() - { - Console.WriteLine("This is a basic test"); - } - - [Test] - [Arguments(1, 2, 3)] - [Arguments(2, 3, 5)] - public async Task DataDrivenArguments(int a, int b, int c) - { - Console.WriteLine("This one can accept arguments from an attribute"); - - var result = a + b; - - await Assert.That(result).IsEqualTo(c); - } - - [Test] - [MethodDataSource(nameof(DataSource))] - public async Task MethodDataSource(int a, int b, int c) - { - Console.WriteLine("This one can accept arguments from a method"); - - var result = a + b; - - await Assert.That(result).IsEqualTo(c); - } - - [Test] - [ClassDataSource] - [ClassDataSource(Shared = SharedType.PerClass)] - [ClassDataSource(Shared = SharedType.PerAssembly)] - [ClassDataSource(Shared = SharedType.PerTestSession)] - public void ClassDataSource(DataClass dataClass) - { - Console.WriteLine("This test can accept a class, which can also be pre-initialised before being injected in"); - - Console.WriteLine("These can also be shared among other tests, or new'd up each time, by using the `Shared` property on the attribute"); - } - - [Test] - [DataGenerator] - public async Task CustomDataGenerator(int a, int b, int c) - { - Console.WriteLine("You can even define your own custom data generators"); - - var result = a + b; - - await Assert.That(result).IsEqualTo(c); - } - - public static IEnumerable<(int a, int b, int c)> DataSource() - { - yield return (1, 1, 2); - yield return (2, 1, 3); - yield return (3, 1, 4); - } -} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Tests2.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Tests2.cs deleted file mode 100644 index b8f3a30eff..0000000000 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Tests2.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace TUnit; - -[Arguments("Hello")] -[Arguments("World")] -public class MoreTests(string title) -{ - [Test] - public void ClassLevelDataRow() - { - Console.WriteLine(title); - Console.WriteLine(@"Did I forget that data injection works on classes too?"); - } - - // You can even inject in ClassDataSources as properties to avoid repetitive constructors if you're using inheritance! - [ClassDataSource(Shared = SharedType.PerTestSession)] - public required DataClass DataClass { get; init; } - - [Test] - [MatrixDataSource] - public void Matrices( - [Matrix(1, 2, 3)] int a, - [Matrix(true, false)] bool b, - [Matrix("A", "B", "C")] string c) - { - Console.WriteLine(@"A new test will be created for each data row, whether it's on the class or method level!"); - - Console.WriteLine(@"Oh and this is a matrix test. That means all combinations of inputs are attempted."); - } -} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Tests3.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Tests3.cs deleted file mode 100644 index d419765d46..0000000000 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/Tests3.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace TUnit; - -[ClassDataSource] -[ClassConstructor] -public class AndEvenMoreTests(DataClass dataClass) -{ - [Test] - public void HaveFun() - { - Console.WriteLine(dataClass); - Console.WriteLine("For more information, check out the documentation"); - Console.WriteLine("https://tunit.dev/"); - } -} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithFSharp.TUnit.FSharp._.verified/TUnit.FSharp/TUnit.FSharp.fsproj b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithFSharp.TUnit.FSharp._.verified/TUnit.FSharp/TUnit.FSharp.fsproj index 365e089cf9..fdc08116c8 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithFSharp.TUnit.FSharp._.verified/TUnit.FSharp/TUnit.FSharp.fsproj +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithFSharp.TUnit.FSharp._.verified/TUnit.FSharp/TUnit.FSharp.fsproj @@ -5,7 +5,7 @@ enable enable Exe - net8.0 + net10.0 diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithVB.TUnit.VB._.verified/TUnit.VB/TUnit.VB.vbproj b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithVB.TUnit.VB._.verified/TUnit.VB/TUnit.VB.vbproj index 882ef0f69b..e319c608bf 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithVB.TUnit.VB._.verified/TUnit.VB/TUnit.VB.vbproj +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithVB.TUnit.VB._.verified/TUnit.VB/TUnit.VB.vbproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 latest enable enable diff --git a/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj b/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj index c0886d3e24..48ab04daf8 100644 --- a/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj +++ b/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj @@ -5,7 +5,7 @@ enable enable Exe - net8.0 + net10.0 diff --git a/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj b/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj index 4a67fd0fb5..05972f3c47 100644 --- a/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj +++ b/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj @@ -4,7 +4,7 @@ enable enable Exe - net8.0 + net10.0 diff --git a/TUnit.Templates/content/TUnit.VB/TestProject.vbproj b/TUnit.Templates/content/TUnit.VB/TestProject.vbproj index 5a5d9e4963..17e997feff 100644 --- a/TUnit.Templates/content/TUnit.VB/TestProject.vbproj +++ b/TUnit.Templates/content/TUnit.VB/TestProject.vbproj @@ -1,7 +1,7 @@ - net8.0 + net10.0 latest enable enable diff --git a/docs/docs/assertions/type-checking.md b/docs/docs/assertions/type-checking.md index d4619674ed..07fe2038bf 100644 --- a/docs/docs/assertions/type-checking.md +++ b/docs/docs/assertions/type-checking.md @@ -77,6 +77,35 @@ public async Task Not_Assignable() } ``` +### IsAssignableFrom + +Tests that a value of the specified type can be assigned to a variable of this value's type. This is the reverse of `IsAssignableTo`: + +```csharp +[Test] +public async Task Assignable_From_Derived() +{ + Animal animal = GetAnimal(); + + // Animal variable can accept a Dog value + await Assert.That(animal).IsAssignableFrom(); +} +``` + +### IsNotAssignableFrom + +Tests that a value of the specified type cannot be assigned to a variable of this value's type: + +```csharp +[Test] +public async Task Not_Assignable_From() +{ + Dog dog = GetDog(); + + await Assert.That(dog).IsNotAssignableFrom(); +} +``` + ## Delegate Return Types Type assertions also work on delegate return values, letting you verify the type returned by a method or lambda: diff --git a/docs/src/components/AssertionsLibrary/index.tsx b/docs/src/components/AssertionsLibrary/index.tsx index 7ec9bd84e2..1111e4474b 100644 --- a/docs/src/components/AssertionsLibrary/index.tsx +++ b/docs/src/components/AssertionsLibrary/index.tsx @@ -65,6 +65,7 @@ const assertionsData: Assertion[] = [ { name: 'IsTypeOf', category: 'Type', description: 'Asserts the value is exactly the specified type', syntax: 'await Assert.That(actual).IsTypeOf()', example: 'await Assert.That(obj).IsTypeOf()' }, { name: 'IsAssignableTo', category: 'Type', description: 'Asserts the value is assignable to the specified type', syntax: 'await Assert.That(actual).IsAssignableTo()', example: 'await Assert.That(derived).IsAssignableTo()' }, { name: 'IsAssignableFrom', category: 'Type', description: 'Asserts the type is assignable from the value', syntax: 'await Assert.That(actual).IsAssignableFrom()', example: 'await Assert.That(baseInstance).IsAssignableFrom()' }, + { name: 'IsNotAssignableFrom', category: 'Type', description: 'Asserts the type is not assignable from the value', syntax: 'await Assert.That(actual).IsNotAssignableFrom()', example: 'await Assert.That(dog).IsNotAssignableFrom()' }, // DateTime { name: 'IsAfter', category: 'DateTime', description: 'Asserts the date is after the expected date', syntax: 'await Assert.That(actual).IsAfter(expected)', example: 'await Assert.That(DateTime.Now).IsAfter(yesterday)' },