Skip to main content

Matrix Tests

The Matrix data source is a way to specify different arguments per parameter, and then generate every possible combination of all of those arguments.

warning

As your number of arguments and/or parameters increase, the number of test cases will grow exponentially.
For example, 3 parameters with 10 values each will generate 1,000 test cases (10 × 10 × 10).
Use with caution to avoid very large test suites.

For our arguments, we'll add a [Matrix] attribute. Instead of this being added to the test method, it's added to the parameters themselves.

And for the test method, we'll add a [MatrixDataSource] attribute which contains the logic to extract out all the data from those parameter Matrix attributes.

Here's an example:

using TUnit.Assertions;
using TUnit.Assertions.Extensions;
using TUnit.Assertions.Extensions.Is;
using TUnit.Core;

namespace MyTestProject;

public class MyTestClass
{
[Test]
[MatrixDataSource]
public async Task MyTest(
[Matrix(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)] int value1,
[Matrix(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)] int value2
)
{
var result = Add(value1, value2);

await Assert.That(result).IsPositive();
}

private int Add(int x, int y)
{
return x + y;
}
}

That will generate 100 test cases. 10 different values for value1, and 10 different values for value2. 10*10 is 100.

Matrix Range

You can also use the [MatrixRange<T>] for numerical types. It will generated a range between the minimum and maximum, with an optional step parameter to define how far to step between each value. By default, this is 1.

using TUnit.Assertions;
using TUnit.Assertions.Extensions;
using TUnit.Assertions.Extensions.Is;
using TUnit.Core;

namespace MyTestProject;

public class MyTestClass
{
[Test]
[MatrixDataSource]
public async Task MyTest(
[MatrixRange<int>(1, 10)] int value1,
[MatrixRange<int>(1, 10)] int value2
)
{
var result = Add(value1, value2);

await Assert.That(result).IsPositive();
}

private int Add(int x, int y)
{
return x + y;
}
}

Matrix Method

You can also specify a method that will return an IEnumerable<T> of values.

using TUnit.Assertions;
using TUnit.Assertions.Extensions;
using TUnit.Assertions.Extensions.Is;
using TUnit.Core;

namespace MyTestProject;

public class MyTestClass
{
[Test]
[MatrixDataSource]
public async Task MyTest(
[MatrixRange<int>(1, 10)] int value1,
[MatrixMethod(nameof(Numbers))] int value2
)
{
var result = Add(value1, value2);

await Assert.That(result).IsPositive();
}

private int Add(int x, int y)
{
return x + y;
}

private IEnumerable<int> Numbers()
{
yield return 1;
yield return 2;
yield return 3;
yield return 4;
yield return 5;
yield return 6;
yield return 7;
yield return 8;
yield return 9;
yield return 10;
}
}

Matrix Exclusions

You can also add a [MatrixExclusion(...)] attribute to your tests. This works similar to the [Arguments(...)] attribute, and if objects match a generated matrix test case, it'll be ignored.

This helps you exclude specific one-off scenarios without having to complicate your tests with if conditions.

info

Use exclusions to keep your test matrix manageable and avoid unnecessary or invalid test cases.

using TUnit.Assertions;
using TUnit.Assertions.Extensions;
using TUnit.Assertions.Extensions.Is;
using TUnit.Core;

namespace MyTestProject;

public class MyTestClass
{
[Test]
[MatrixDataSource]
[MatrixExclusion(1, 1)]
[MatrixExclusion(2, 2)]
[MatrixExclusion(3, 3)]
public async Task MyTest(
[MatrixRange<int>(1, 3)] int value1,
[MatrixRange<int>(1, 3)] int value2
)
{
...
}
}

Whereas the above Matrix would usually generate:

  • 1, 1
  • 1, 2
  • 1, 3
  • 2, 1
  • 2, 2
  • 2, 3
  • 3, 1
  • 3, 2
  • 3, 3

Because of the exclusion attributes, it'll only generate:

  • 1, 2
  • 1, 3
  • 2, 1
  • 2, 3
  • 3, 1
  • 3, 2