Deffiss / testenvironment-docker

MIT License
117 stars 30 forks source link

IAssemblyFixture issue. Tests execution does not end. #55

Closed dmitry-kosar closed 3 years ago

dmitry-kosar commented 3 years ago

I’m having an issue when using IAssemblyFixture(from https://www.nuget.org/packages/Xunit.Extensions.AssemblyFixture) for EnvironmentFixture. I works fine on my local machine, but not on CI pipeline. It does not throw any error it just runs and does not end.

Also IClassFixture works fine, but I want to share environment between several tests.

Here is GitHub action

runs-on: ubuntu-latest
timeout-minutes: 5

steps:
- uses: actions/checkout@v2.3.4

- name: Setup .NET Core 5.0.x
  uses: actions/setup-dotnet@v1.8.1
  with:
    dotnet-version: 5.0.x

- name: Restore dependencies
  run: dotnet restore

- name: Build
  run: dotnet build --configuration Debug --no-restore

- name: Test
  run: dotnet test --configuration Debug --no-build --verbosity normal

Usage

public class MyTests : IAssemblyFixture<EnvironmentFixture>
{
    private readonly EnvironmentFixture _environmentFixture;

    public MyTests(EnvironmentFixture environmentFixture)
    {
        _environmentFixture = environmentFixture;
    }
….

EnveronmentFixture

public class EnvironmentFixture : IAsyncLifetime, IDisposable
{
    private readonly string _environmentName;
    private DockerEnvironment _environment;
    private IHost _host;

    public HttpClient TestClient { get; private set; }

    public EnvironmentFixture()
    {
        _environmentName = "test";
        Task.WaitAll(InitializeAsync());
    }

    public async Task InitializeAsync()
    {
        // Docker environment setup.
        _environment = CreateTestEnvironmentBuilder().Build();
        await _environment.Up();

        // API Test host setup
        _host = await CreateHostBuilder().StartAsync();

        TestClient = _host.GetTestClient();

        await OnInitialized(_environment.GetContainer<MongoContainer>("mongo"));
    }

I have mongo, redis and kafka containers registering following way

builder.AddContainer(name, "redis", "6-alpine");
builder.AddContainer(name, "docker.io/bitnami/kafka", "2-debian-10”);
builder.AddMongoContainer("mongo”);
Deffiss commented 3 years ago

Hi @dmitry-kosar I think you don't have to call InitializeAsync() from your constructor. It is executed by the xunit framework once per run.

dgvives commented 3 years ago

Hello @dmitry-kosar Is the CI pipeline running the same arch as you do locally?

dmitry-kosar commented 3 years ago

Hello @dmitry-kosar Is the CI pipeline running the same arch as you do locally?

Hello, @dgvives I'm running tests locally on MBP Intel Core i5 macOs Catalina. And CI runs on ubuntu-latest

dmitry-kosar commented 3 years ago

Hi @dmitry-kosar I think you don't have to call InitializeAsync() from your constructor. It is executed by the xunit framework once per run.

@Deffiss According to IAssebmlyFixture documentation I need to put initialization code into constructor. 😔 See https://github.com/JDCain/Xunit.Extensions.AssemblyFixture#:~:text=public%20DatabaseFixture()

Taturevich commented 3 years ago

You probably have a deadlock in this part code:

public EnvironmentFixture()
{
    _environmentName = "test";
    Task.WaitAll(InitializeAsync());
}

This will certainly fire in a CI environment. The only way I managed to share the context across different classes is to initialize it once in the first test and store it in a separate class as context. Subsequent tests will check whether it was initialized. And if it was then it will reuse it.

dgvives commented 3 years ago

@dmitry-kosar I confirm you do not need to call OnInitializeAsync from the constructor. In our case, we initialize a logger, but not the environment itself.

        public TestEnvironmentFixture()
        {
            Logger = new TestLogger<TestEnvironmentFixture>();
        }

I suppose this is just a typo, to write environmentTwoFixture where you mean environmentFixture


public class MyTests : IAssemblyFixture<EnvironmentFixture>
{
    private readonly EnvironmentFixture _environmentFixture;

    public DirectIntegrationQueryTests(EnvironmentFixture environmentFixture)
    {
        _environmentFixture = environmentFixture;
        // _environmentFixture = environmentTwoFixture;
    }
….
dmitry-kosar commented 3 years ago

Finally I managed to make it work using IColletionFixture

   [CollectionDefinition("Environment collection")]
    public class EnvironmentCollection : ICollectionFixture<EnvironmentFixture> { }
    [Collection("Environment collection")]
    public class MyTests
    {
        private readonly EnvironmentFixture _environmentFixture;

        public MyTests(EnvironmentFixture environmentFixture)
        {
            _environmentFixture = environmentFixture;
        }
...