Skip to content
Merged
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ $ GIT_USER=<Your GitHub username> yarn deploy
```

If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.

Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
---
sidebar_position: 2
---

# Awaiting

In TUnit you `await` your assertions, and this serves two purposes:
Expand All @@ -10,7 +6,8 @@ In TUnit you `await` your assertions, and this serves two purposes:

Because of this, your tests should be `async` and return a `Task`.

Don't worry about forgetting to `await` - There's an analyzer built in that will notify you if you've missed any!
Don't worry about forgetting to `await` - There's an analyzer built in that will notify you if you've missed any!
If you forget to `await`, your assertion will not actually be executed, and your test may pass when it should fail.

This will error:

Expand All @@ -37,4 +34,4 @@ This won't:
```

TUnit is able to take in asynchronous delegates. To be able to assert on these, we need to execute the code. We want to avoid sync-over-async, as this can cause problems and block the thread pool, slowing down your test suite.
And with how fast .NET has become, the overhead of `Task`s and `async` methods shouldn't be noticable.
And with how fast .NET has become, the overhead of `Task`s and `async` methods shouldn't be noticable.
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ TUnit can execute your delegates for you, and this allows you to assert on the d
return Task.CompletedTask;
});
}
```
```
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,4 @@ public class StringEqualsExpectedValueAssertCondition(string expected, StringCom
$"found \"{actualValue}\"");
}
}
```
```
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
---
sidebar_position: 2
---

# Chaining and Converting

We may want to chain assertions together that change the type of object being asserted, to keep the assertions reading clear and concise, and not having to declare more variables and more boiler-plate assert calls.
TUnit allows you to chain assertions that change the type being asserted, enabling fluent and expressive test code.
This is useful when an assertion transforms the value (e.g., parsing a response), and you want to continue asserting on the new type.

This is possible in TUnit.
Chaining is especially helpful when you want to perform multiple assertions on a value that is transformed by a previous assertion, without having to create intermediate variables.

For example:

```csharp
HttpResponseMessage response = ...;

await Assert.That(response)
.IsProblemDetails()
.And
Expand Down Expand Up @@ -86,4 +85,4 @@ public static class ProblemDetailsAssertionExtensions
return valueSource.RegisterAssertion(new ProblemDetailsHasDetailAssertCondition(detail), [detailExpression]);
}
}
```
```
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
---
sidebar_position: 3
---

# Returning Data via `await`

Sometimes, you may want your assertion to return a value, such as an item found in a collection, so you can use it in further assertions or logic.
TUnit supports this by allowing your assertion to return a mapped result when awaited.

This is useful for scenarios where you want to extract a value from an assertion and use it in subsequent test logic, reducing the need for manual extraction or casting.

It may make sense for our assertions to return data that is different from the input, based on what the assertion is doing. This can allow more cleanly written tests than have to manually do casting or parsing afterwards.

For example, `await Assert.That(collection).Contains(item => item.Price < 0.99)`
Expand Down Expand Up @@ -84,4 +85,4 @@ public static MappableResultAssertionBuilder<IEnumerable<TInner>, EnumerableCont
(_, assertCondition) => assertCondition.FoundItem
);
}
```
```
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ When using this, only one condition needs to pass:
.Or.IsEqualTo(3)
.Or.IsEqualTo(4);
}
```
```
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
---
sidebar_position: 3
---

# Assertion Scopes

In TUnit you can create an assertion scope by calling `Assert.Multiple()`. This returns an `IDisposable` and so you should use that by encapsulating the returned value in a `using` block. This will make sure that any assertion exceptionss are aggregated together and thrown only after the scope is exited.
Expand Down Expand Up @@ -38,3 +34,4 @@ Explicit Scope:
}
}
```

5 changes: 1 addition & 4 deletions docs/docs/comparison/attributes.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
---
sidebar_position: 1
---

# Attributes

Here are TUnit's equivalent attributes to other test frameworks.
Expand Down Expand Up @@ -62,3 +58,4 @@ Here are TUnit's equivalent attributes to other test frameworks.
| ---------- | ---------------------- | ---------- | -------------- |
| [Category] | [Trait("Category","")] | [Category] | [TestCategory] |
| [Property] | [Trait] | [Property] | [TestProperty] |

11 changes: 6 additions & 5 deletions docs/docs/comparison/framework-differences.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
---
sidebar_position: 2
---

# Framework Differences

TUnit is inspired by NUnit and xUnit, and first and foremost I want to say that these are amazing frameworks and no hate to them.

**Why use TUnit?**
TUnit aims to address some pain points and limitations found in other frameworks, especially around parallelism, lifecycle hooks, test isolation, and extensibility.
Below are some scenarios where TUnit offers a different or improved approach.

So you'll be asking why use TUnit instead of them, right?
Here are some things I've stumbled across in the past that I've found limiting when writing a test suite.

Expand Down Expand Up @@ -119,4 +120,4 @@ In TUnit, you can use a `[DependsOn(...)]` attribute. That test will wait to sta
```

### Class Arguments
A lot of the data injection mechanisms in xUnit/NUnit work for the method, or the class, and not vice-versa. With TUnit, you can use `[Arguments(...)]` or `[Matrix(...)]` or `[MethodDataSource(...)]` etc. for both classes and test methods, making it super flexible!
A lot of the data injection mechanisms in xUnit/NUnit work for the method, or the class, and not vice-versa. With TUnit, you can use `[Arguments(...)]` or `[Matrix(...)]` or `[MethodDataSource(...)]` etc. for both classes and test methods, making it super flexible!
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
---
sidebar_position: 16
---

# Argument Formatters

If you are writing data driven tests, and using custom classes to represent your data, then the test explorer might not show you useful information to distinguish test cases, and instead only show you the class name.
When writing data-driven tests, especially with custom classes as arguments, the test explorer may only show the class name, making it hard to distinguish test cases.
Argument formatters let you control how arguments are displayed in test names and test explorers.

If you want control over how injected arguments appear in the test explorer, you can create a class that inherits from `ArgumentDisplayFormatter` and then decorate your test with the `[ArgumentDisplayFormatter<T>]` attribute.

Expand Down Expand Up @@ -34,4 +31,9 @@ public class MyFormatter : ArgumentDisplayFormatter
return $"One: {someClass.One} | Two: {someClass.Two}";
}
}
```
```

:::info
You can apply multiple `[ArgumentDisplayFormatter<T>]` attributes if you have different types to format.
The first formatter whose `CanHandle` returns true will be used.
:::
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
---
sidebar_position: 13
---

# Data Source Generators

TUnit exposes an `abstract` `DataSourceGeneratorAttribute` class that you can inherit from and implement your own logic for creating values.
Expand Down Expand Up @@ -72,4 +68,4 @@ The `TestBuilderContext` object exposes `Events` - And you can register a delega
dataGeneratorMetadata.TestBuilderContext.Current; // <-- A new object again
}

```
```
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
---
sidebar_position: 15
---

# Display Names

If you want simple control over the name of a test, you can use the `[DisplayName(...)]` attribute.
Expand All @@ -15,7 +11,7 @@ If you want simple control over the name of a test, you can use the `[DisplayNam
}
```

This is also able to reference parameters by using `$parameterName` within the attribute.
You can reference test parameters in the display name by using `$parameterName` within the attribute string. At runtime, these will be replaced with the actual argument values for each test case.

```csharp
[Test]
Expand All @@ -32,7 +28,12 @@ The above would generate two test cases with their respective display name as:
- "Test with: foo 1 True"
- "Test with: bar 2 False"

If you have custom classes, you can combine this with [Argument Formatters](./argument-formatters.md) to specify how to show them.
If you have custom classes, you can combine this with [Argument Formatters](customization-extensibility/argument-formatters.md) to specify how to show them.

:::info
If you want to include a literal `$` in your display name, escape it as `$$`.
For example: `[DisplayName("Total cost: $$100")]` will display as "Total cost: $100".
:::

## Custom Logic

Expand All @@ -41,4 +42,4 @@ If you want to have more control over how your test names are, you can create an
There you will find a method that you must override: `FormatDisplayName`.
Here you have access to all the arguments and test details via the `TestContext` parameter.

Then simply add that custom attribute to your test.
Then simply add that custom attribute to your test.
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
---
sidebar_position: 8
---

# Libraries

If you want a library package to define things like re-useable base classes with hooks etc, then you shouldn't use the main `TUnit` package - As this assumes your project is a test project and tries to build it as an executable etc.

Instead, reference `TUnit.Core` instead - It has all of the models required for wiring up your tests, but without all the extra setting up of the test suite execition.
Instead, reference `TUnit.Core` instead - It has all of the models required for wiring up your tests, but without all the extra setting up of the test suite execition.
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
---
sidebar_position: 20
---

# Logging

By default, TUnit will intercept any logs to the `Console`, and attempt to correlate them to the test that triggered that log by the current async context that it is in.
Expand All @@ -23,4 +19,4 @@ If you want to override this, you can inherit from `TUnitLogger` or `DefaultLogg
{
return logLevel >= LogLevel.Error;
}
```
```
6 changes: 1 addition & 5 deletions docs/docs/examples/aspnet.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
---
sidebar_position: 2
---

# ASP.NET Core Web App/Api

If you want to test a web app, you can utilise the Microsoft.Mvc.Testing packages to wrap your web app within an in-memory test server.
Expand Down Expand Up @@ -46,4 +42,4 @@ public class MyTests
}
```

Alternatively, you can use the `[NotInParallel]` attribute to avoid parallelism and multi-initialisation. But you'll most likely be sacrificing test speeds if tests can't run in parallel.
Alternatively, you can use the `[NotInParallel]` attribute to avoid parallelism and multi-initialisation. But you'll most likely be sacrificing test speeds if tests can't run in parallel.
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
---
sidebar_position: 31
---

# F# Interactive

F# Interactive (FSI) is a REPL (Read-Eval-Print Loop) for F#. It allows you to execute F# code interactively, making it a powerful tool for testing and prototyping.
Expand Down Expand Up @@ -92,3 +88,4 @@ format. To use TUnit with F# Interactive, follow these steps:
```powershell
dotnet fsi your_script.fsx
```

Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
---
sidebar_position: 3
---

# Instrumenting: Global Test IDs

There are plenty use cases for having a unique identifier for each test in your test suite. If you're engineering tests that connect to a data source, you might want to ensure data isolation between the tests. One way to do that is cleaning up the data source after each test, but that prevents you from running tests cleanly in parallel, and it requires you to write either very intelligent or a lot of cleanup code. Additionally, bugs can cause data to leak between tests and make your tests flaky.
Expand Down Expand Up @@ -89,3 +85,4 @@ class MyTestClassThatNeedsUniqueTestIds
```

The test identifier for each test is assigned in the order that TUnit discovers the tests. The test identifier is unique for each test and is guaranteed to be assigned before the test starts. For other uses cases, you would need to adjust the implementation of `AssignTestIdentifiersAttribute` to suit your needs. For example, you could choose to use GUIDs instead of integers. We've only used integers to match the Redis database number example.

6 changes: 1 addition & 5 deletions docs/docs/examples/intro.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
---
sidebar_position: 0
---

# Examples

This can serve as a place to show how to use TUnit to test more complex systems, utilising advanced features like ClassData sources with IAsyncInitializers and IAsyncDisposables, or utilising test events to drive things.

As tests come in all shapes and sizes, this is a good place for community contributions. If you have a good example for showing other users how to setup a specific testing scenario, then please submit a pull request with code examples.
As tests come in all shapes and sizes, this is a good place for community contributions. If you have a good example for showing other users how to setup a specific testing scenario, then please submit a pull request with code examples.
6 changes: 1 addition & 5 deletions docs/docs/examples/playwright.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
---
sidebar_position: 1
---

# Playwright

There is a NuGet package to help with Playwright: `TUnit.Playwright`
Expand Down Expand Up @@ -33,4 +29,4 @@ You can override the `BrowserName` to control which browser you want to launch.
The possible values are:
- chromium
- firefox
- webkit
- webkit
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
---
sidebar_position: 4
---

# TUnit in CI pipelines

When using TUnit for end-to-end (E2E) tests or TUnit's Playwright library for UI testing, you will likely run these tests in a CI/CD pipeline—either on a schedule or as part of a release. In such cases, it is important to publish the test results for visibility and reporting.
Expand Down Expand Up @@ -30,3 +26,4 @@ steps:
> Best Practice:
> For efficiency and clearity in failures, separate restore, build, and test into distinct steps.
> A common approach is to perform restore and build in a "build pipeline", then execute tests using --no-build in a separate "test pipeline" to avoid redundant compilation and improve performance.

Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
---
sidebar_position: 30
---

# Engine Modes

In some scenarios, source generation may not be available for you.
Expand All @@ -18,4 +14,4 @@ However, if you need to manually set this, then in your `.csproj` add the follow
<PropertyGroup>
<TUnitReflectionScanner>true</TUnitReflectionScanner>
</PropertyGroup>
```
```
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
---
sidebar_position: 8
---

# Test Filters

Running TUnit via `dotnet run` supports test filters.
Expand Down Expand Up @@ -29,8 +25,9 @@ or

`dotnet run --treenode-filter /*/*/*/AcceptCookiesTest` - To run all tests with the name `AcceptCookiesTest`

TUnit also supports filtering by your own [properties](properties). So you could do:
TUnit also supports filtering by your own [properties](../test-lifecycle/properties.md). So you could do:

`dotnet run --treenode-filter /*/*/*/*[MyFilterName=*SomeValue*]`

And if your test had a property with the name "MyFilterName" and its value contained "SomeValue", then your test would be executed.

6 changes: 1 addition & 5 deletions docs/docs/experimental/dynamic-tests.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
---
sidebar_position: 1
---

# Dynamically Created Tests

TUnit offers the ability to create your tests via dynamic code, as opposed to the standard `[Test]` attribute and data attributes.
Expand Down Expand Up @@ -78,4 +74,4 @@ public class Runtime(int a, int b, int c)
});
}
}
```
```
Loading
Loading