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
6 changes: 6 additions & 0 deletions TUnit.Templates.Tests/BasicTemplateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ public async Task InstantiationTest()
await Engine.Execute(Options).ConfigureAwait(false);
}

[Test]
public async Task InstantiationTestWithNetFramework()
{
await Engine.Execute(OptionsWithFramework("net48")).ConfigureAwait(false);
}

[Test]
public async Task InstantiationTestWithFSharp()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace TUnit;

public class GlobalHooks
public static class GlobalHooks
{
[Before(TestSession)]
public static Task BeforeTestSession(TestSessionContext context)
Expand Down
Original file line number Diff line number Diff line change
@@ -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.");
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace TUnit;

public class AdditionDataGeneratorAttribute : DataSourceGeneratorAttribute<int, int, int>
{
protected override IEnumerable<Func<(int, int, int)>> GenerateDataSources(
DataGeneratorMetadata dataGeneratorMetadata)
{
yield return () => (1, 1, 2);
yield return () => (4, 5, 9);
yield return () => (-1, 1, 0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using TUnit.Core.Interfaces;

namespace TUnit;

public class InMemoryDb : IAsyncInitializer, IAsyncDisposable
{
private Dictionary<string, string> _store = null!;

public Task InitializeAsync()
{
// Simulate async setup - e.g. connecting to a database or starting a container
_store = new Dictionary<string, string>();
return Task.CompletedTask;
}

public Task SetAsync(string key, string value)
{
_store[key] = value;
return Task.CompletedTask;
}

public Task<string?> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace TUnit;

public class DependencyInjectionTests
{
// A new InMemoryDb is created for each test
[Test]
[ClassDataSource<InMemoryDb>]
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<InMemoryDb>(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<InMemoryDb>(Shared = SharedType.PerTestSession)]
public class SharedDatabaseTests(InMemoryDb db)
{
// Or injected as a property instead of a constructor parameter
[ClassDataSource<Calculator>]
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");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

[assembly: TUnit.Polyfills.ExcludeFromCodeCoverage]

namespace TUnit;

public static 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;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace TUnit.Polyfills;

[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event, Inherited = false, AllowMultiple = false)]
internal sealed class ExcludeFromCodeCoverageAttribute : Attribute
{
public string? Justification { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<TargetFramework>net48</TargetFramework>
<LangVersion>preview</LangVersion>
<EnableTUnitPolyfills>false</EnableTUnitPolyfills>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="TUnit" Version="0.0.0-scrubbed" />
<PackageReference Include="Polyfill" Version="0.0.0-scrubbed" PrivateAssets="all" />
</ItemGroup>

</Project>
12 changes: 12 additions & 0 deletions TUnit.Templates.Tests/TemplateTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ public abstract partial class TemplateTestBase : IDisposable
.AddScrubber(ScrubVersions, "vbproj")
);

protected TemplateVerifierOptions OptionsWithFramework(string framework) =>
new TemplateVerifierOptions(TemplateShortName)
{
TemplatePath = Path.Combine(TestContext.OutputDirectory!, "content", TemplateShortName),
TemplateSpecificArgs = ["--framework", framework],
}.WithCustomScrubbers(
ScrubbersDefinition.Empty
.AddScrubber(ScrubVersions, "csproj")
.AddScrubber(ScrubVersions, "fsproj")
.AddScrubber(ScrubVersions, "vbproj")
);

private const string ScrubbedVersion = "0.0.0-scrubbed";

private static void ScrubVersions(StringBuilder sb)
Expand Down
36 changes: 35 additions & 1 deletion TUnit.Templates/content/TUnit/.template.config/template.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,40 @@
"type": "project"
},
"sourceName": "TestProject",
"preferNameDirectory": true
"preferNameDirectory": true,
"symbols": {
"framework": {
"type": "parameter",
"description": "The target framework for the project.",
"datatype": "choice",
"choices": [
{ "choice": "net10.0", "description": "Target .NET 10.0" },
{ "choice": "net9.0", "description": "Target .NET 9.0" },
{ "choice": "net8.0", "description": "Target .NET 8.0" },
{ "choice": "net481", "description": "Target .NET Framework 4.8.1" },
{ "choice": "net48", "description": "Target .NET Framework 4.8" },
{ "choice": "net472", "description": "Target .NET Framework 4.7.2" },
{ "choice": "net462", "description": "Target .NET Framework 4.6.2" }
],
"defaultValue": "net10.0",
"replaces": "net10.0"
},
"IsNetFramework": {
"type": "computed",
"value": "(framework == \"net462\" || framework == \"net472\" || framework == \"net48\" || framework == \"net481\")"
}
},
"sources": [
{
"modifiers": [
{
"condition": "(!IsNetFramework)",
"exclude": [
"Polyfills/**"
]
}
]
}
]
}

8 changes: 7 additions & 1 deletion TUnit.Templates/content/TUnit/HooksAndLifecycle.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
#if (!IsNetFramework)
using System.Diagnostics.CodeAnalysis;
#endif

#if (IsNetFramework)
[assembly: TestProject.Polyfills.ExcludeFromCodeCoverage]
#else
[assembly: ExcludeFromCodeCoverage]
#endif

namespace TestProject;

public class GlobalHooks
public static class GlobalHooks
{
[Before(TestSession)]
public static Task BeforeTestSession(TestSessionContext context)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace TestProject.Polyfills;

[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event, Inherited = false, AllowMultiple = false)]
internal sealed class ExcludeFromCodeCoverageAttribute : Attribute
{
public string? Justification { get; set; }
}
Loading
Loading