diff --git a/README.md b/README.md
index 24aa8b38a3..d663cfc3d2 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,15 @@ TUnit is designed to aid with all testing types:
[![nuget](https://img.shields.io/nuget/v/TUnit.svg)](https://www.nuget.org/packages/TUnit/) [![NuGet Downloads](https://img.shields.io/nuget/dt/TUnit)](https://www.nuget.org/packages/TUnit/)
![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/thomhurst/TUnit/dotnet.yml) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/thomhurst/TUnit/main) ![License](https://img.shields.io/github/license/thomhurst/TUnit)
+## Quick Start
+
+Assuming you have the .NET SDK installed, simply run:
+
+`dotnet new install TUnit.Templates`
+`dotnet new TUnit -n "YourProjectName"`
+
+A new test project will be created for you with some samples of different test types and tips. When you're ready to get going, delete them and create your own!
+
## Documentation
See here:
diff --git a/README_Template.md b/README_Template.md
index 96b9085d54..5be14f1559 100644
--- a/README_Template.md
+++ b/README_Template.md
@@ -16,6 +16,15 @@ TUnit is designed to aid with all testing types:
[![nuget](https://img.shields.io/nuget/v/TUnit.svg)](https://www.nuget.org/packages/TUnit/) [![NuGet Downloads](https://img.shields.io/nuget/dt/TUnit)](https://www.nuget.org/packages/TUnit/)
![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/thomhurst/TUnit/dotnet.yml) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/thomhurst/TUnit/main) ![License](https://img.shields.io/github/license/thomhurst/TUnit)
+## Quick Start
+
+Assuming you have the .NET SDK installed, simply run:
+
+`dotnet new install TUnit.Templates`
+`dotnet new TUnit -n "YourProjectName"`
+
+A new test project will be created for you with some samples of different test types and tips. When you're ready to get going, delete them and create your own!
+
## Documentation
See here:
diff --git a/TUnit.Pipeline/Modules/GetPackageProjectsModule.cs b/TUnit.Pipeline/Modules/GetPackageProjectsModule.cs
index 22ebd6c9ca..08ec190586 100644
--- a/TUnit.Pipeline/Modules/GetPackageProjectsModule.cs
+++ b/TUnit.Pipeline/Modules/GetPackageProjectsModule.cs
@@ -1,6 +1,4 @@
using ModularPipelines.Context;
-using ModularPipelines.Extensions;
-using ModularPipelines.Git.Extensions;
using ModularPipelines.Modules;
using File = ModularPipelines.FileSystem.File;
@@ -8,18 +6,18 @@ namespace TUnit.Pipeline.Modules;
public class GetPackageProjectsModule : Module>
{
- protected override Task?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
+ protected override async Task?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
{
- return context.Git().RootDirectory
- .GetFiles(x => x.Extension == ".csproj")
- .Where(x => !x.Name.Contains("Pipeline", StringComparison.OrdinalIgnoreCase))
- .Where(x => !x.Name.Contains("Analyzer", StringComparison.OrdinalIgnoreCase))
- .Where(x => !x.Name.Contains("Generator", StringComparison.OrdinalIgnoreCase))
- .Where(x => !x.Name.Contains("Sample", StringComparison.OrdinalIgnoreCase))
- .Where(x => !x.Name.Contains("Test", StringComparison.OrdinalIgnoreCase))
- .Where(x => !x.Name.Contains("Timer", StringComparison.OrdinalIgnoreCase))
- .Where(x => !x.Name.Contains("CodeFix", StringComparison.OrdinalIgnoreCase))
- .ToList()
- .AsTask?>();
+ await Task.CompletedTask;
+
+ return
+ [
+ Sourcy.DotNet.Projects.TUnit_Assertions,
+ Sourcy.DotNet.Projects.TUnit_Core,
+ Sourcy.DotNet.Projects.TUnit_Engine,
+ Sourcy.DotNet.Projects.TUnit,
+ Sourcy.DotNet.Projects.TUnit_Playwright,
+ Sourcy.DotNet.Projects.TUnit_Templates
+ ];
}
}
\ No newline at end of file
diff --git a/TUnit.Pipeline/Modules/PackTUnitFilesModule.cs b/TUnit.Pipeline/Modules/PackTUnitFilesModule.cs
index c61ad091a7..b67c9589cb 100644
--- a/TUnit.Pipeline/Modules/PackTUnitFilesModule.cs
+++ b/TUnit.Pipeline/Modules/PackTUnitFilesModule.cs
@@ -37,7 +37,7 @@ await context.DotNet()
new KeyValue("AssemblyFileVersion", version.SemVer!),
new KeyValue("IsPackTarget", "true")
],
- IncludeSource = true,
+ IncludeSource = project == Sourcy.DotNet.Projects.TUnit_Templates ? false : true,
Configuration = Configuration.Release,
}, cancellationToken);
diff --git a/TUnit.Pipeline/TUnit.Pipeline.csproj b/TUnit.Pipeline/TUnit.Pipeline.csproj
index b8cc35811c..7a2e8af8f5 100644
--- a/TUnit.Pipeline/TUnit.Pipeline.csproj
+++ b/TUnit.Pipeline/TUnit.Pipeline.csproj
@@ -4,6 +4,12 @@
false
false
+
+
+
+ <_Parameter1>$(MSBuildProjectFullPath)
+
+
Exe
diff --git a/TUnit.Templates/README.md b/TUnit.Templates/README.md
new file mode 100644
index 0000000000..f28f7c600a
--- /dev/null
+++ b/TUnit.Templates/README.md
@@ -0,0 +1,5 @@
+# TUnit.Templates
+
+Some templates to help you get started with TUnit!
+
+For more information, check out the repository at https://www.github.com/thomhurst/TUnit
\ No newline at end of file
diff --git a/TUnit.Templates/TUnit.Templates.csproj b/TUnit.Templates/TUnit.Templates.csproj
new file mode 100644
index 0000000000..3665784a95
--- /dev/null
+++ b/TUnit.Templates/TUnit.Templates.csproj
@@ -0,0 +1,39 @@
+
+
+
+ TUnit.Templates
+ 1.0
+ TUnit Templates
+ Tom Longhurst
+ Templates for getting started with TUnit
+ TUnit,test,testing,unit,integration,acceptance
+ https://www.github.com/thomhurst/TUnit
+
+ Template
+ net8.0
+ true
+ false
+ content
+ $(NoWarn);NU5128
+ true
+ README.md
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TUnit.Templates/content/Directory.Build.props b/TUnit.Templates/content/Directory.Build.props
new file mode 100644
index 0000000000..91a726632a
--- /dev/null
+++ b/TUnit.Templates/content/Directory.Build.props
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TUnit.Templates/content/Directory.Build.targets b/TUnit.Templates/content/Directory.Build.targets
new file mode 100644
index 0000000000..0c98d167d1
--- /dev/null
+++ b/TUnit.Templates/content/Directory.Build.targets
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/TUnit.Templates/content/Directory.Packages.props b/TUnit.Templates/content/Directory.Packages.props
new file mode 100644
index 0000000000..5f9708a97f
--- /dev/null
+++ b/TUnit.Templates/content/Directory.Packages.props
@@ -0,0 +1,5 @@
+
+
+ false
+
+
diff --git a/TUnit.Templates/content/TUnit/.template.config/template.json b/TUnit.Templates/content/TUnit/.template.config/template.json
new file mode 100644
index 0000000000..772e07bd94
--- /dev/null
+++ b/TUnit.Templates/content/TUnit/.template.config/template.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "http://json.schemastore.org/template",
+ "author": "Tom Longhurst",
+ "description": "Templates for getting started with TUnit",
+ "classifications": [
+ "Test",
+ "TUnit"
+ ],
+ "name": "TUnit Test Project",
+ "identity": "TUnit.Test.Project",
+ "groupIdentity": "TUnit",
+ "shortName": "TUnit",
+ "tags": {
+ "language": "C#",
+ "type": "project"
+ },
+ "sourceName": "TestProject",
+ "preferNameDirectory": true
+ }
+
\ No newline at end of file
diff --git a/TUnit.Templates/content/TUnit/Data/DataClass.cs b/TUnit.Templates/content/TUnit/Data/DataClass.cs
new file mode 100644
index 0000000000..4a15d696b3
--- /dev/null
+++ b/TUnit.Templates/content/TUnit/Data/DataClass.cs
@@ -0,0 +1,16 @@
+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.");
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Templates/content/TUnit/Data/DataGenerator.cs b/TUnit.Templates/content/TUnit/Data/DataGenerator.cs
new file mode 100644
index 0000000000..9b409f7d17
--- /dev/null
+++ b/TUnit.Templates/content/TUnit/Data/DataGenerator.cs
@@ -0,0 +1,11 @@
+namespace TestProject.Data;
+
+public class DataGenerator : DataSourceGeneratorAttribute
+{
+ public override IEnumerable> GenerateDataSources(DataGeneratorMetadata dataGeneratorMetadata)
+ {
+ yield return () => (1, 1, 2);
+ yield return () => (1, 2, 3);
+ yield return () => (4, 5, 9);
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Templates/content/TUnit/Data/DependencyInjectionClassConstructor.cs b/TUnit.Templates/content/TUnit/Data/DependencyInjectionClassConstructor.cs
new file mode 100644
index 0000000000..82378bd8c0
--- /dev/null
+++ b/TUnit.Templates/content/TUnit/Data/DependencyInjectionClassConstructor.cs
@@ -0,0 +1,18 @@
+using TUnit.Core.Interfaces;
+
+namespace TestProject;
+
+public class DependencyInjectionClassConstructor : IClassConstructor
+{
+ public T Create(ClassConstructorMetadata classConstructorMetadata) where T : class
+ {
+ 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 (typeof(T) == typeof(AndEvenMoreTests))
+ {
+ return (new AndEvenMoreTests(new DataClass()) as T)!;
+ }
+
+ throw new NotImplementedException();
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Templates/content/TUnit/GlobalSetup.cs b/TUnit.Templates/content/TUnit/GlobalSetup.cs
new file mode 100644
index 0000000000..442534393d
--- /dev/null
+++ b/TUnit.Templates/content/TUnit/GlobalSetup.cs
@@ -0,0 +1,21 @@
+// 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)]
+
+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!");
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Templates/content/TUnit/TestProject.csproj b/TUnit.Templates/content/TUnit/TestProject.csproj
new file mode 100644
index 0000000000..29c022516f
--- /dev/null
+++ b/TUnit.Templates/content/TUnit/TestProject.csproj
@@ -0,0 +1,14 @@
+
+
+
+ enable
+ enable
+ Exe
+ net8.0
+
+
+
+
+
+
+
diff --git a/TUnit.Templates/content/TUnit/Tests.cs b/TUnit.Templates/content/TUnit/Tests.cs
new file mode 100644
index 0000000000..5425b19b0a
--- /dev/null
+++ b/TUnit.Templates/content/TUnit/Tests.cs
@@ -0,0 +1,65 @@
+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);
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Templates/content/TUnit/Tests2.cs b/TUnit.Templates/content/TUnit/Tests2.cs
new file mode 100644
index 0000000000..c7aba7c85b
--- /dev/null
+++ b/TUnit.Templates/content/TUnit/Tests2.cs
@@ -0,0 +1,28 @@
+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]
+ 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.");
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Templates/content/TUnit/Tests3.cs b/TUnit.Templates/content/TUnit/Tests3.cs
new file mode 100644
index 0000000000..be621ab1f6
--- /dev/null
+++ b/TUnit.Templates/content/TUnit/Tests3.cs
@@ -0,0 +1,14 @@
+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://thomhurst.github.io/TUnit/");
+ }
+}
\ No newline at end of file
diff --git a/TUnit.sln b/TUnit.sln
index ce4b089df7..bd95cdede5 100644
--- a/TUnit.sln
+++ b/TUnit.sln
@@ -69,6 +69,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TUnit.Analyzers.Roslyn47",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TUnit.Engine.Tests", "TUnit.Engine.Tests\TUnit.Engine.Tests.csproj", "{E0E07E64-BC0A-489E-B562-2982F3836994}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "TUnit.Templates\content\TUnit\TestProject.csproj", "{4BFDDFDB-96D9-42AA-85CE-9FBD017ACBE1}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -183,6 +185,10 @@ Global
{E0E07E64-BC0A-489E-B562-2982F3836994}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E0E07E64-BC0A-489E-B562-2982F3836994}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E0E07E64-BC0A-489E-B562-2982F3836994}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4BFDDFDB-96D9-42AA-85CE-9FBD017ACBE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4BFDDFDB-96D9-42AA-85CE-9FBD017ACBE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4BFDDFDB-96D9-42AA-85CE-9FBD017ACBE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4BFDDFDB-96D9-42AA-85CE-9FBD017ACBE1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -211,6 +217,7 @@ Global
{744CD312-B913-48E0-B917-531ED2A9A541} = {503DA9FA-045D-4910-8AF6-905E6048B1F1}
{54D5F1A7-7979-4C07-8FE1-426233846018} = {503DA9FA-045D-4910-8AF6-905E6048B1F1}
{E0E07E64-BC0A-489E-B562-2982F3836994} = {62AD1EAF-43C4-4AC0-B9FA-CD59739B3850}
+ {4BFDDFDB-96D9-42AA-85CE-9FBD017ACBE1} = {0BA988BF-ADCE-4343-9098-B4EF65C43709}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {109D285A-36B3-4503-BCDF-8E26FB0E2C5B}
diff --git a/docs/docs/tutorial-basics/installing.md b/docs/docs/tutorial-basics/installing.md
index 73b74f2ebf..ef05889af2 100644
--- a/docs/docs/tutorial-basics/installing.md
+++ b/docs/docs/tutorial-basics/installing.md
@@ -4,6 +4,17 @@ sidebar_position: 1
# Installing TUnit
+## Easily
+
+Assuming you have the .NET SDK installed, simply run:
+
+`dotnet new install TUnit.Templates`
+`dotnet new TUnit -n "YourProjectName"`
+
+A new test project will be created for you with some samples of different test types and tips. When you're ready to get going, delete them and create your own!
+
+## Manually
+
First create an empty .NET console application:
```powershell