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
33 changes: 25 additions & 8 deletions TUnit.Core/Attributes/ClassConstructorAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
using System.Diagnostics.CodeAnalysis;
using System;
using System.Diagnostics.CodeAnalysis;
using TUnit.Core.Interfaces;

namespace TUnit.Core;

public abstract class ClassConstructorAttribute : TUnitAttribute, IDataAttribute
// Base abstract class
public abstract class BaseClassConstructorAttribute : TUnitAttribute, IDataAttribute
{
public abstract Type ClassConstructorType { get; }
public abstract Type ClassConstructorType { get; set; }

internal ClassConstructorAttribute()
private protected BaseClassConstructorAttribute() { }
private protected BaseClassConstructorAttribute(Type classType) { ClassConstructorType = classType; }
}

// Single sealed attribute with both generic and non-generic constructors
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class)]
public class ClassConstructorAttribute : BaseClassConstructorAttribute
{
public ClassConstructorAttribute(Type classConstructorType)
: base(classConstructorType)
{
ClassConstructorType = classConstructorType;
}

public override Type ClassConstructorType { get; set; }
}

// Generic version for C#
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class)]
public sealed class ClassConstructorAttribute<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>
: ClassConstructorAttribute where T : IClassConstructor, new()
public sealed class ClassConstructorAttribute<
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>()
: ClassConstructorAttribute(typeof(T))
where T : IClassConstructor, new()
{
public override Type ClassConstructorType { get; } = typeof(T);
}
public override Type ClassConstructorType { get; set; } = typeof(T);
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ namespace TUnit.Core
public static TUnit.Core.AsyncEvent<TEventArgs> operator +(TUnit.Core.AsyncEvent<TEventArgs>? e, System.Func<object, TEventArgs, System.Threading.Tasks.Task> callback) { }
public static TUnit.Core.AsyncEvent<TEventArgs>? operator -(TUnit.Core.AsyncEvent<TEventArgs>? e, System.Func<object, TEventArgs, System.Threading.Tasks.Task> callback) { }
}
public abstract class BaseClassConstructorAttribute : TUnit.Core.TUnitAttribute, TUnit.Core.IDataAttribute
{
public abstract System.Type ClassConstructorType { get; set; }
}
[System.AttributeUsage(System.AttributeTargets.Method)]
public abstract class BaseTestAttribute : TUnit.Core.TUnitAttribute
{
Expand Down Expand Up @@ -116,16 +120,18 @@ namespace TUnit.Core
public int Order { get; }
public void OnTestDiscovery(TUnit.Core.DiscoveredTestContext discoveredTestContext) { }
}
public abstract class ClassConstructorAttribute : TUnit.Core.TUnitAttribute, TUnit.Core.IDataAttribute
[System.AttributeUsage(System.AttributeTargets.Assembly | System.AttributeTargets.Class)]
public class ClassConstructorAttribute : TUnit.Core.BaseClassConstructorAttribute
{
public abstract System.Type ClassConstructorType { get; }
public ClassConstructorAttribute(System.Type classConstructorType) { }
public override System.Type ClassConstructorType { get; set; }
}
[System.AttributeUsage(System.AttributeTargets.Assembly | System.AttributeTargets.Class)]
public sealed class ClassConstructorAttribute<T> : TUnit.Core.ClassConstructorAttribute
where T : TUnit.Core.Interfaces.IClassConstructor, new ()
{
public ClassConstructorAttribute() { }
public override System.Type ClassConstructorType { get; }
public override System.Type ClassConstructorType { get; set; }
}
public class ClassConstructorMetadata : System.IEquatable<TUnit.Core.ClassConstructorMetadata>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ namespace TUnit.Core
public static TUnit.Core.AsyncEvent<TEventArgs> operator +(TUnit.Core.AsyncEvent<TEventArgs>? e, System.Func<object, TEventArgs, System.Threading.Tasks.Task> callback) { }
public static TUnit.Core.AsyncEvent<TEventArgs>? operator -(TUnit.Core.AsyncEvent<TEventArgs>? e, System.Func<object, TEventArgs, System.Threading.Tasks.Task> callback) { }
}
public abstract class BaseClassConstructorAttribute : TUnit.Core.TUnitAttribute, TUnit.Core.IDataAttribute
{
public abstract System.Type ClassConstructorType { get; set; }
}
[System.AttributeUsage(System.AttributeTargets.Method)]
public abstract class BaseTestAttribute : TUnit.Core.TUnitAttribute
{
Expand Down Expand Up @@ -116,16 +120,18 @@ namespace TUnit.Core
public int Order { get; }
public void OnTestDiscovery(TUnit.Core.DiscoveredTestContext discoveredTestContext) { }
}
public abstract class ClassConstructorAttribute : TUnit.Core.TUnitAttribute, TUnit.Core.IDataAttribute
[System.AttributeUsage(System.AttributeTargets.Assembly | System.AttributeTargets.Class)]
public class ClassConstructorAttribute : TUnit.Core.BaseClassConstructorAttribute
{
public abstract System.Type ClassConstructorType { get; }
public ClassConstructorAttribute(System.Type classConstructorType) { }
public override System.Type ClassConstructorType { get; set; }
}
[System.AttributeUsage(System.AttributeTargets.Assembly | System.AttributeTargets.Class)]
public sealed class ClassConstructorAttribute<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] T> : TUnit.Core.ClassConstructorAttribute
where T : TUnit.Core.Interfaces.IClassConstructor, new ()
{
public ClassConstructorAttribute() { }
public override System.Type ClassConstructorType { get; }
public override System.Type ClassConstructorType { get; set; }
}
public class ClassConstructorMetadata : System.IEquatable<TUnit.Core.ClassConstructorMetadata>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ namespace TUnit.Core
public static TUnit.Core.AsyncEvent<TEventArgs> operator +(TUnit.Core.AsyncEvent<TEventArgs>? e, System.Func<object, TEventArgs, System.Threading.Tasks.Task> callback) { }
public static TUnit.Core.AsyncEvent<TEventArgs>? operator -(TUnit.Core.AsyncEvent<TEventArgs>? e, System.Func<object, TEventArgs, System.Threading.Tasks.Task> callback) { }
}
public abstract class BaseClassConstructorAttribute : TUnit.Core.TUnitAttribute, TUnit.Core.IDataAttribute
{
public abstract System.Type ClassConstructorType { get; set; }
}
[System.AttributeUsage(System.AttributeTargets.Method)]
public abstract class BaseTestAttribute : TUnit.Core.TUnitAttribute
{
Expand Down Expand Up @@ -116,16 +120,18 @@ namespace TUnit.Core
public int Order { get; }
public void OnTestDiscovery(TUnit.Core.DiscoveredTestContext discoveredTestContext) { }
}
public abstract class ClassConstructorAttribute : TUnit.Core.TUnitAttribute, TUnit.Core.IDataAttribute
[System.AttributeUsage(System.AttributeTargets.Assembly | System.AttributeTargets.Class)]
public class ClassConstructorAttribute : TUnit.Core.BaseClassConstructorAttribute
{
public abstract System.Type ClassConstructorType { get; }
public ClassConstructorAttribute(System.Type classConstructorType) { }
public override System.Type ClassConstructorType { get; set; }
}
[System.AttributeUsage(System.AttributeTargets.Assembly | System.AttributeTargets.Class)]
public sealed class ClassConstructorAttribute<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] T> : TUnit.Core.ClassConstructorAttribute
where T : TUnit.Core.Interfaces.IClassConstructor, new ()
{
public ClassConstructorAttribute() { }
public override System.Type ClassConstructorType { get; }
public override System.Type ClassConstructorType { get; set; }
}
public class ClassConstructorMetadata : System.IEquatable<TUnit.Core.ClassConstructorMetadata>
{
Expand Down
12 changes: 12 additions & 0 deletions TUnit.TestProject.FSharp/ClassConstructorTest.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace TUnit.TestProject

open TUnit.Core

[<ClassConstructor(typeof<DependencyInjectionClassConstructor>)>]
type ClassConstructorTest(dummyReferenceTypeClass: DummyReferenceTypeClass) =

member _.DummyReferenceTypeClass = dummyReferenceTypeClass

[<Test>]
member _.Test() = ()

28 changes: 28 additions & 0 deletions TUnit.TestProject.FSharp/ClassConstructorWithEnumerableTest.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace TUnit.TestProject.FSharp

open System
open Microsoft.Extensions.DependencyInjection
open TUnit
open TUnit.TestProject
open TUnit.Core

[<ClassConstructor(typeof<DependencyInjectionClassConstructor>)>]
[<NotInParallel>]
type ClassConstructorWithEnumerableTest(services: IServiceProvider) =
let mutable isDisposed = false

[<Before(HookType.Test)>]
member _.Setup() =
if isDisposed then
raise (ObjectDisposedException(nameof(ClassConstructorWithEnumerableTest)))

[<Test>]
[<MethodDataSource("GetValues")>]
member _.DoSomething(value: int) =
ActivatorUtilities.GetServiceOrCreateInstance<DummyReferenceTypeClass>(services) |> ignore

static member GetValues() : seq<int> = seq { yield 1; yield 2; yield 3; yield 4 }

interface IDisposable with
member _.Dispose() =
isDisposed <- true
26 changes: 26 additions & 0 deletions TUnit.TestProject.FSharp/DependencyInjectionClassConstructor.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace TUnit.TestProject

open System
open System.Threading.Tasks
open Microsoft.Extensions.DependencyInjection
open TUnit.Core.Interfaces

type DependencyInjectionClassConstructor() =
let serviceProvider: IServiceProvider =
ServiceCollection()
.AddTransient<DummyReferenceTypeClass>()
.BuildServiceProvider()
let mutable scope : AsyncServiceScope option = None

interface IClassConstructor with
member _.Create(typ, _) =
if scope.IsNone then
scope <- Some(serviceProvider.CreateAsyncScope())
ActivatorUtilities.GetServiceOrCreateInstance(scope.Value.ServiceProvider, typ)

interface ITestEndEventReceiver with
member _.OnTestEnd(_testContext) =
match scope with
| Some s -> s.DisposeAsync()
| None -> ValueTask()
member _.Order = 0
4 changes: 4 additions & 0 deletions TUnit.TestProject.FSharp/DummyReferenceTypeClass.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace TUnit.TestProject

type DummyReferenceTypeClass() = class end

5 changes: 5 additions & 0 deletions TUnit.TestProject.FSharp/TUnit.TestProject.FSharp.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,17 @@
</ItemGroup>

<ItemGroup>
<Compile Include="DummyReferenceTypeClass.fs" />
<Compile Include="DependencyInjectionClassConstructor.fs" />
<Compile Include="ClassConstructorTest.fs" />
<Compile Include="ClassConstructorWithEnumerableTest.fs" />
<Compile Include="ClassDataSourceDrivenTests.fs" />
<Compile Include="Tests.fs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="FSharp.Core" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" />
<PackageReference Include="Microsoft.Testing.Extensions.TrxReport" />
</ItemGroup>
Expand Down
17 changes: 17 additions & 0 deletions TUnit.TestProject.VB.NET/ClassConstructorTest.vb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Imports TUnit.Core

<ClassConstructor(GetType(DependencyInjectionClassConstructor))>
Public Class ClassConstructorTest

Public Sub New(dummyReferenceTypeClass As DummyReferenceTypeClass)
Me.DummyReferenceTypeClass = dummyReferenceTypeClass
End Sub

Public ReadOnly Property DummyReferenceTypeClass As DummyReferenceTypeClass

<Test>
Public Sub Test()
' Test logic here
End Sub

End Class
38 changes: 38 additions & 0 deletions TUnit.TestProject.VB.NET/ClassConstructorWithEnumerableTest.vb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
Imports System
Imports Microsoft.Extensions.DependencyInjection
Imports TUnit
Imports TUnit.Core

<ClassConstructor(GetType(DependencyInjectionClassConstructor))>
<NotInParallel>
Public NotInheritable Class ClassConstructorWithEnumerableTest
Implements IDisposable

Private _isDisposed As Boolean
Private ReadOnly _services As IServiceProvider

Public Sub New(services As IServiceProvider)
Me._services = services
End Sub

<Before(HookType.Test)>
Public Sub Setup()
If _isDisposed Then
Throw New ObjectDisposedException(NameOf(ClassConstructorWithEnumerableTest))
End If
End Sub

<Test>
<MethodDataSource(NameOf(GetValues))>
Public Sub DoSomething(value As Integer)
ActivatorUtilities.GetServiceOrCreateInstance(Of DummyReferenceTypeClass)(_services)
End Sub

Public Shared Function GetValues() As IEnumerable(Of Integer)
Return New Integer() {1, 2, 3, 4}
End Function

Public Sub Dispose() Implements IDisposable.Dispose
_isDisposed = True
End Sub
End Class
36 changes: 36 additions & 0 deletions TUnit.TestProject.VB.NET/DependencyInjectionClassConstructor.vb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
Imports System
Imports System.Threading.Tasks
Imports Microsoft.Extensions.DependencyInjection
Imports TUnit.Core
Imports TUnit.Core.Interfaces

Public Class DependencyInjectionClassConstructor
Implements IClassConstructor
Implements ITestEndEventReceiver

Private ReadOnly _serviceProvider As IServiceProvider = CreateServiceProvider()
Private _scope As AsyncServiceScope? = Nothing

Public Function Create(type As Type, classConstructorMetadata As ClassConstructorMetadata) As Object Implements IClassConstructor.Create
If _scope Is Nothing Then
_scope = _serviceProvider.CreateAsyncScope()
End If
Return ActivatorUtilities.GetServiceOrCreateInstance(_scope.Value.ServiceProvider, type)
End Function

Public Function OnTestEnd(testContext As AfterTestContext) As ValueTask Implements ITestEndEventReceiver.OnTestEnd
Return _scope.Value.DisposeAsync()
End Function

Private Shared Function CreateServiceProvider() As IServiceProvider
Return New ServiceCollection().
AddTransient(Of DummyReferenceTypeClass)().
BuildServiceProvider()
End Function

Public ReadOnly Property Order As Integer Implements IEventReceiver.Order
Get
Return 0
End Get
End Property
End Class
3 changes: 3 additions & 0 deletions TUnit.TestProject.VB.NET/DummyReferenceTypeClass.vb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Public Class DummyReferenceTypeClass

End Class
1 change: 1 addition & 0 deletions TUnit.TestProject.VB.NET/TUnit.TestProject.VB.NET.vbproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" />
<PackageReference Include="Microsoft.Testing.Extensions.TrxReport" />
</ItemGroup>
Expand Down
Loading