Skip to content

Commit ad73307

Browse files
authored
Add support for TUnit Class Constructors (#1362)
1 parent fd58c7b commit ad73307

9 files changed

+59
-29
lines changed

src/Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.0" />
1717
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
1818
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
19+
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
1920
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
2021
<PackageVersion Include="NUnit" Version="4.2.2" />
2122
<PackageVersion Include="MSTest" Version="3.6.3" />
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using TUnit.Core.Interfaces;
3+
4+
public class ClassConstructor : IClassConstructor
5+
{
6+
static readonly IServiceProvider serviceProvider = CreateServiceProvider();
7+
8+
AsyncServiceScope scope;
9+
10+
public T Create<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(ClassConstructorMetadata classConstructorMetadata)
11+
where T : class
12+
{
13+
scope = serviceProvider.CreateAsyncScope();
14+
return ActivatorUtilities.GetServiceOrCreateInstance<T>(scope.ServiceProvider);
15+
}
16+
17+
static IServiceProvider CreateServiceProvider() =>
18+
new ServiceCollection()
19+
.AddSingleton<string>("SingletonValue")
20+
.BuildServiceProvider();
21+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SingletonValue
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[ClassConstructor<ClassConstructor>]
2+
[Arguments("2")]
3+
public class ClassConstructorMixedWithArgumentTests(string service)
4+
{
5+
[Test]
6+
public async Task Test()
7+
{
8+
await Assert.That(service).IsNotNull();
9+
await Verify(service);
10+
}
11+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SingletonValue
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[ClassConstructor<ClassConstructor>]
2+
public class ClassConstructorTests(string service)
3+
{
4+
[Test]
5+
public async Task Test()
6+
{
7+
await Assert.That(service).IsNotNull();
8+
await Verify(service);
9+
}
10+
}

src/Verify.TUnit.Tests/Verify.TUnit.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
1314
<PackageReference Include="ProjectDefaults" PrivateAssets="all" />
1415

1516
<ProjectReference Include="..\TargetLibrary\TargetLibrary.csproj" />

src/Verify.TUnit/TUnitExtensions.cs

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,34 @@ static class TUnitExtensions
44
{
55
var methodParameterNames = details.MethodInfo.ParameterNames();
66

7-
var names = GetConstructorParameterNames(details.ClassType, details.TestClassArguments.Length)
8-
.ToList();
7+
var constructorParameterNames = GetConstructorParameterNames(details);
98
if (methodParameterNames == null)
109
{
11-
if (names.Count == 0)
10+
if (constructorParameterNames.Count == 0)
1211
{
1312
return null;
1413
}
1514

16-
return names;
15+
return constructorParameterNames;
1716
}
1817

19-
if (names.Count == 0 &&
20-
methodParameterNames.Count == 0)
18+
if (constructorParameterNames.Count == 0 &&
19+
methodParameterNames.Count == 0)
2120
{
2221
return null;
2322
}
2423

25-
return [.. names, .. methodParameterNames];
24+
return [.. constructorParameterNames, .. methodParameterNames];
2625
}
2726

28-
public static IEnumerable<string> GetConstructorParameterNames(this Type type, int argumentsLength)
27+
static List<string> GetConstructorParameterNames(TestDetails details)
2928
{
30-
IEnumerable<string>? names = null;
31-
foreach (var constructor in type.GetConstructors(BindingFlags.Instance | BindingFlags.Public))
29+
var constructors = details.ClassType.GetConstructors(BindingFlags.Instance | BindingFlags.Public);
30+
if (constructors.Length <= 1)
3231
{
33-
var parameters = constructor.GetParameters();
34-
if (parameters.Length != argumentsLength)
35-
{
36-
continue;
37-
}
38-
39-
if (names != null)
40-
{
41-
throw new($"Found multiple constructors with {argumentsLength} parameters. Unable to derive names of parameters. Instead use UseParameters to pass in explicit parameter.");
42-
}
43-
44-
names = parameters.Select(_ => _.Name!);
45-
}
46-
47-
if (names == null)
48-
{
49-
throw new($"Could not find constructor with {argumentsLength} parameters.");
32+
return constructors[0].GetParameters().Select(_ => _.Name!).ToList();
5033
}
5134

52-
return names;
35+
throw new("Found multiple constructors. Unable to derive names of parameters. Instead use UseParameters to pass in explicit parameter.");
5336
}
54-
}
37+
}

0 commit comments

Comments
 (0)