diff --git a/TUnit.Templates/content/TUnit/BasicTests.cs b/TUnit.Templates/content/TUnit/BasicTests.cs new file mode 100644 index 0000000000..38890143f0 --- /dev/null +++ b/TUnit.Templates/content/TUnit/BasicTests.cs @@ -0,0 +1,53 @@ +namespace TestProject; + +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/content/TUnit/Calculator.cs b/TUnit.Templates/content/TUnit/Calculator.cs new file mode 100644 index 0000000000..dffe2813a1 --- /dev/null +++ b/TUnit.Templates/content/TUnit/Calculator.cs @@ -0,0 +1,10 @@ +namespace TestProject; + +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/content/TUnit/Data/AdditionDataGenerator.cs b/TUnit.Templates/content/TUnit/Data/AdditionDataGenerator.cs new file mode 100644 index 0000000000..f3e22e9e35 --- /dev/null +++ b/TUnit.Templates/content/TUnit/Data/AdditionDataGenerator.cs @@ -0,0 +1,12 @@ +namespace TestProject; + +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/content/TUnit/Data/DataClass.cs b/TUnit.Templates/content/TUnit/Data/DataClass.cs deleted file mode 100644 index 38998dd176..0000000000 --- a/TUnit.Templates/content/TUnit/Data/DataClass.cs +++ /dev/null @@ -1,16 +0,0 @@ -using TUnit.Core.Interfaces; - -namespace TestProject; - -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/content/TUnit/Data/DataSourceGenerator.cs b/TUnit.Templates/content/TUnit/Data/DataSourceGenerator.cs deleted file mode 100644 index 9c4e45baaa..0000000000 --- a/TUnit.Templates/content/TUnit/Data/DataSourceGenerator.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace TestProject.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/content/TUnit/Data/DependencyInjectionClassConstructor.cs b/TUnit.Templates/content/TUnit/Data/DependencyInjectionClassConstructor.cs deleted file mode 100644 index 8fc1ece42a..0000000000 --- a/TUnit.Templates/content/TUnit/Data/DependencyInjectionClassConstructor.cs +++ /dev/null @@ -1,18 +0,0 @@ -using TUnit.Core.Interfaces; - -namespace TestProject; - -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/content/TUnit/Data/InMemoryDb.cs b/TUnit.Templates/content/TUnit/Data/InMemoryDb.cs new file mode 100644 index 0000000000..bbefe41632 --- /dev/null +++ b/TUnit.Templates/content/TUnit/Data/InMemoryDb.cs @@ -0,0 +1,34 @@ +using TUnit.Core.Interfaces; + +namespace TestProject; + +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/content/TUnit/DataDrivenTests.cs b/TUnit.Templates/content/TUnit/DataDrivenTests.cs new file mode 100644 index 0000000000..45ef287b35 --- /dev/null +++ b/TUnit.Templates/content/TUnit/DataDrivenTests.cs @@ -0,0 +1,77 @@ +namespace TestProject; + +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/content/TUnit/DependencyInjectionTests.cs b/TUnit.Templates/content/TUnit/DependencyInjectionTests.cs new file mode 100644 index 0000000000..e2d1ad9f2a --- /dev/null +++ b/TUnit.Templates/content/TUnit/DependencyInjectionTests.cs @@ -0,0 +1,47 @@ +namespace TestProject; + +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/content/TUnit/GlobalSetup.cs b/TUnit.Templates/content/TUnit/GlobalSetup.cs deleted file mode 100644 index 7aeee0f582..0000000000 --- a/TUnit.Templates/content/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 TestProject; - -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/content/TUnit/HooksAndLifecycle.cs b/TUnit.Templates/content/TUnit/HooksAndLifecycle.cs new file mode 100644 index 0000000000..a47c4182ad --- /dev/null +++ b/TUnit.Templates/content/TUnit/HooksAndLifecycle.cs @@ -0,0 +1,23 @@ +using System.Diagnostics.CodeAnalysis; + +[assembly: ExcludeFromCodeCoverage] + +namespace TestProject; + +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/content/TUnit/TestProject.csproj b/TUnit.Templates/content/TUnit/TestProject.csproj index 9ef6c5759a..ec0997dbe7 100644 --- a/TUnit.Templates/content/TUnit/TestProject.csproj +++ b/TUnit.Templates/content/TUnit/TestProject.csproj @@ -1,14 +1,14 @@ - + enable enable Exe - net8.0 + net10.0 - + - \ No newline at end of file + diff --git a/TUnit.Templates/content/TUnit/Tests.cs b/TUnit.Templates/content/TUnit/Tests.cs deleted file mode 100644 index e7f1577ae4..0000000000 --- a/TUnit.Templates/content/TUnit/Tests.cs +++ /dev/null @@ -1,65 +0,0 @@ -using TestProject.Data; - -namespace TestProject; - -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/content/TUnit/Tests2.cs b/TUnit.Templates/content/TUnit/Tests2.cs deleted file mode 100644 index 9492b5cf7a..0000000000 --- a/TUnit.Templates/content/TUnit/Tests2.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace TestProject; - -[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/content/TUnit/Tests3.cs b/TUnit.Templates/content/TUnit/Tests3.cs deleted file mode 100644 index 1a1ffe3d70..0000000000 --- a/TUnit.Templates/content/TUnit/Tests3.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace TestProject; - -[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/"); - } -}