Skip to content
Open
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
50 changes: 50 additions & 0 deletions docs/core/testing/mstest-analyzers/mstest0049.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,56 @@ This rule triggers when:

Use the provided code fixer to automatically pass the <xref:System.Threading.CancellationToken> from <xref:Microsoft.VisualStudio.TestTools.UnitTesting.TestContext> to method calls that support cancellation. You can also manually update method calls to include `TestContext.CancellationToken` as a parameter.

### Example: TestContext property

The following code triggers MSTEST0049 because `Task.Delay` and `HttpClient.GetAsync` accept a <xref:System.Threading.CancellationToken> but none is passed:

```csharp
[TestClass]
public class MyTestClass
{
public TestContext TestContext { get; set; }

[TestMethod]
public async Task TestMethod()
{
using var client = new HttpClient();
// MSTEST0049: these calls accept a CancellationToken but none is passed
await Task.Delay(1000);
var response = await client.GetAsync("https://example.com");
}
}
```

The fix is to pass `TestContext.CancellationToken` to each call:

:::code language="csharp" source="../snippets/testcontext/csharp-cancellation/CancellationToken.cs":::

### Example: constructor injection (MSTest 3.6+)

With constructor injection, use the `_testContext` field to access the cancellation token:

:::code language="csharp" source="../snippets/testcontext/csharp-cancellation/CancellationTokenCtor.cs":::

## When to suppress warnings

Suppress this warning if you intentionally don't want to support cancellation for specific operations, or if the operation should continue even when the test is cancelled.

## Suppress a warning

If you just want to suppress a single violation, add preprocessor directives to your source file to disable and then re-enable the rule.

```csharp
#pragma warning disable MSTEST0049
// The code that's violating the rule is on this line.
#pragma warning restore MSTEST0049
```

To disable the rule for a file, folder, or project, set its severity to `none` in the [configuration file](../../../fundamentals/code-analysis/configuration-files.md).

```ini
[*.{cs,vb}]
dotnet_diagnostic.MSTEST0049.severity = none
```

For more information, see [How to suppress code analysis warnings](../../../fundamentals/code-analysis/suppress-warnings.md).
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class TestClassCancellationToken
{
// MSTest automatically sets the TestContext property before each test runs.
// MSTest.Analyzers includes a diagnostic suppressor that removes CS8618
// (non-nullable property uninitialized) for this property.
public TestContext TestContext { get; set; }

[TestMethod]
[Timeout(5000, CooperativeCancellation = true)]
public async Task MyAsyncTest()
{
using var client = new HttpClient();
var response = await client.GetAsync(
"https://example.com", TestContext.CancellationToken);

Assert.IsTrue(response.IsSuccessStatusCode);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class TestClassCancellationTokenCtor
{
private readonly TestContext _testContext;

public TestClassCancellationTokenCtor(TestContext testContext)
{
_testContext = testContext;
}

[TestMethod]
[Timeout(5000, CooperativeCancellation = true)]
public async Task MyAsyncTest()
{
using var client = new HttpClient();
var response = await client.GetAsync(
"https://example.com", _testContext.CancellationToken);

Assert.IsTrue(response.IsSuccessStatusCode);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MSTest" Version="4.1.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,21 @@ The <xref:Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.AddResultFile

You can also use <xref:Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.Write*?displayProperty=nameWithType> or <xref:Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.WriteLine*?displayProperty=nameWithType> methods to write custom messages directly to the test output. This is especially useful for debugging purposes, as it provides real-time logging information within your test execution context.

### Cancellation token

The <xref:Microsoft.VisualStudio.TestTools.UnitTesting.TestContext> exposes a <xref:Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.CancellationToken> property that is signaled when the test times out or the test run is aborted. You should pass this token to async operations so that they can respond to cancellation cooperatively. This is especially important when using [Timeout](xref:Microsoft.VisualStudio.TestTools.UnitTesting.TimeoutAttribute) attributes.

When <xref:Microsoft.VisualStudio.TestTools.UnitTesting.TestContext> is accessed as a property:

:::code language="csharp" source="snippets/testcontext/csharp-cancellation/CancellationToken.cs":::

When <xref:Microsoft.VisualStudio.TestTools.UnitTesting.TestContext> is injected through the constructor (MSTest 3.6+):

:::code language="csharp" source="snippets/testcontext/csharp-cancellation/CancellationTokenCtor.cs":::

> [!TIP]
> MSTest analyzer rule [MSTEST0049](mstest-analyzers/mstest0049.md) helps identify async calls where `TestContext.CancellationToken` should be passed. It also provides a code fixer to apply the change automatically.

## Related analyzers

The following analyzers help ensure proper usage of the `TestContext` class:
Expand Down
Loading