Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ClassCleanupBehavior.EndOfClass causes Microsoft.AspNetCore.Mvc.Testing based tests to fail with TaskCanceledException: The operation was canceled. #4492

Open
abatishchev opened this issue Jan 1, 2025 · 0 comments

Comments

@abatishchev
Copy link

abatishchev commented Jan 1, 2025

Describe the bug

Test which use an in-memory ASP.NET Core web server and linked HTTP client started failing with the following exception after I replaced [ClassCleanup] with [ClassCleanupBehavior.EndOfClass] per MSTEST0034:

Failed DeleteAccount_Should_Return_Ok [135 ms]
  Error Message:
   Test method Provisioning.Service.Tests.Functional.Controllers.DataControllerTests.DeleteAccount_Should_Return_Ok threw exception: 
System.Threading.Tasks.TaskCanceledException: The operation was canceled. ---> System.Net.Http.HttpRequestException: Error while copying content to a stream. ---> System.IO.IOException:  ---> System.IO.IOException: The client aborted the request.
  Stack Trace:
      at Microsoft.AspNetCore.TestHost.ResponseBodyReaderStream.CheckAborted()
   at Microsoft.AspNetCore.TestHost.ResponseBodyReaderStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
   at System.IO.Stream.<CopyToAsync>g__Core|27_0(Stream source, Stream destination, Int32 bufferSize, CancellationToken cancellationToken)
   at System.Net.Http.HttpContent.LoadIntoBufferAsyncCore(Task serializeToStreamTask, MemoryStream tempBuffer)
--- End of inner exception stack trace ---
    at System.Net.Http.HttpContent.LoadIntoBufferAsyncCore(Task serializeToStreamTask, MemoryStream tempBuffer)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
--- End of inner exception stack trace ---
    at System.Net.Http.HttpClient.HandleFailure(Exception e, Boolean telemetryStarted, HttpResponseMessage response, CancellationTokenSource cts, CancellationToken cancellationToken, CancellationTokenSource pendingRequestsCts)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   at Provisioning.Service.Tests.Functional.Controllers.DataControllerTests.SendAsync[T](HttpMethod httpMethod, Uri url, T resource) in C:\__w\1\s\src\Provisioning\Service.Tests.Functional\Controllers\DataControllerTests.cs:line 103
   at Provisioning.Service.Tests.Functional.Controllers.DataControllerTests.DeleteAccount_Should_Return_Ok() in C:\__w\1\s\src\Provisioning\Service.Tests.Functional\Controllers\DataControllerTests.cs:line 45

Steps To Reproduce

[TestClass]
public class DataControllerTests
{
    protected static WebApplicationFactory<Program> _app;
    protected static HttpClient _httpClient;

    [ClassInitialize]
    public static void ClassInitialize(TestContext context)
    {
        _app = new WebApplicationFactory<Program>()
            .WithWebHostBuilder(builder => builder.ConfigureServices(s =>
            {
                // setting up some mocks
            })
            .UseEnvironment(Environments.Development));
        _httpClient = _app.CreateDefaultClient();
    }

    [ClassCleanup(ClassCleanupBehavior.EndOfClass)] // <-- !
    public static void ClassCleanup() =>
        _app.Dispose();

    [TestMethod]
    public async Task DeleteAccount_Should_Return_Ok()
    {
        var account = CreateAccount();
        var url = new Uri($"/data/accounts/{account.Name}", UriKind.Relative);

        // Act
        using var response = await SendAsync(HttpMethod.Delete, url, account);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
    }

    private static Account CreateAccount() =>
        new Account(); // simple POCO

    private async Task<HttpResponseMessage> SendAsync<T>(
        HttpMethod httpMethod,
        Uri url,
        T resource)
        where T : class
    {
        var request = new HttpRequestMessage(httpMethod, url)
        {
            Content = new JsonContent(resource)
        };
        return await _httpClient.SendAsync(request, this.TestContext!.CancellationTokenSource.Token);
    }
}

Expected behavior

The tests continue working as before.

Actual behavior

See above.

Additional context

Test project:

<PropertyGroup>
  <TargetFramework>net8.0</TargetFramework>
  <Platform>AnyCPU</Platform>
  <IsPackable>false</IsPackable>
  <IsTestProject>true</IsTestProject>
</PropertyGroup>

NuGet packages:

<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.11" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageVersion Include="MSTest.TestAdapter" Version="3.7.0" />
<PackageVersion Include="MSTest.TestFramework" Version="3.7.0" />

<GlobalPackageReference Include="MSTest.Analyzers" Version="3.7.0">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</GlobalPackageReference>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant