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
Original file line number Diff line number Diff line change
Expand Up @@ -321,12 +321,18 @@ private static void GenerateTestMetadata(CodeWriter writer, TestMethodMetadata t
.Any(a => a.AttributeClass?.Name == "MethodDataSourceAttribute" &&
InferClassTypesFromMethodDataSource(compilation, testMethod, a) != null);

if (hasTypedDataSource || hasGenerateGenericTest || testMethod.IsGenericMethod || hasClassArguments || hasTypedDataSourceForGenericType || hasMethodArgumentsForGenericType || hasMethodDataSourceForGenericType)
// Check for class-level data sources that could help resolve generic type arguments
var hasClassDataSources = testMethod.IsGenericType && testMethod.TypeSymbol.GetAttributesIncludingBaseTypes()
.Any(a => DataSourceAttributeHelper.IsDataSourceAttribute(a.AttributeClass));

if (hasTypedDataSource || hasGenerateGenericTest || testMethod.IsGenericMethod || hasClassArguments || hasTypedDataSourceForGenericType || hasMethodArgumentsForGenericType || hasMethodDataSourceForGenericType || hasClassDataSources)
{
GenerateGenericTestWithConcreteTypes(writer, testMethod, className, uniqueClassName);
}
else
{
// For generic classes with no way to resolve type arguments, this will generate
// GenericTestMetadata that the engine will fail with a clear error message
GenerateTestMetadataInstance(writer, testMethod, className, uniqueClassName);
}
}
Expand Down
4 changes: 2 additions & 2 deletions TUnit.Engine/Building/TestBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ await _objectLifecycleService.RegisterObjectAsync(

if (metadata.TestClassType.IsGenericTypeDefinition && resolvedClassGenericArgs.Length == 0)
{
throw new InvalidOperationException($"Cannot create instance of generic type {metadata.TestClassType.Name} with empty type arguments");
throw new InvalidOperationException($"Cannot create test for generic class '{metadata.TestClassType.Name}': No type arguments could be inferred. Add [GenerateGenericTest<ConcreteType>] to the class, or use a data source (like [ClassDataSource<T>] or [Arguments]) that provides constructor arguments to infer the generic type arguments from.");
}

var basicSkipReason = GetBasicSkipReason(metadata, attributes);
Expand Down Expand Up @@ -1727,7 +1727,7 @@ private Task<InstanceCreationResult> CreateInstanceForMethodDataSources(

if (metadata.TestClassType.IsGenericTypeDefinition && resolvedClassGenericArgs.Length == 0)
{
throw new InvalidOperationException($"Cannot create instance of generic type {metadata.TestClassType.Name} with empty type arguments");
throw new InvalidOperationException($"Cannot create test for generic class '{metadata.TestClassType.Name}': No type arguments could be inferred. Add [GenerateGenericTest<ConcreteType>] to the class, or use a data source (like [ClassDataSource<T>] or [Arguments]) that provides constructor arguments to infer the generic type arguments from.");
}

// Create instance factory
Expand Down
7 changes: 5 additions & 2 deletions TUnit.Engine/Services/TestGenericTypeResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ private static Type[] ResolveClassGenericArguments(
// For classes with parameterless constructors, throw a specific exception
// that can be caught and handled by the caller
throw new GenericTypeResolutionException(
$"Could not resolve type for generic parameter(s) of type '{genericClassType.Name}' from constructor arguments. Type inference from method data may be required.");
$"Cannot create test for generic class '{genericClassType.Name}': No type arguments could be inferred. " +
$"Add [GenerateGenericTest<ConcreteType>] to the class, or use a data source " +
$"(like [ClassDataSource<T>] or [Arguments]) that provides constructor arguments to infer the generic type arguments from.");
}

// Resolve all generic parameters
Expand All @@ -108,7 +110,8 @@ private static Type[] ResolveClassGenericArguments(
if (!typeMapping.TryGetValue(genericParam, out var resolvedType))
{
throw new GenericTypeResolutionException(
$"Could not resolve type for generic parameter '{genericParam.Name}' of type '{genericClassType.Name}'");
$"Cannot create test for generic class '{genericClassType.Name}': Could not resolve type for generic parameter '{genericParam.Name}'. " +
$"Add [GenerateGenericTest<ConcreteType>] to the class, or use a data source that provides constructor arguments to infer the generic type arguments from.");
}
resolvedTypes[i] = resolvedType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<InterceptorsNamespaces>$(InterceptorsNamespaces);Microsoft.AspNetCore.OpenApi.Generated</InterceptorsNamespaces>
</PropertyGroup>

<ItemGroup>
Expand Down
Loading