Search code examples
c#asp.net-coreintegration-testingxunitfixtures

xUnit - Using WebApplicationFactory with CollectionFixture for .Net Core Integration Tests


I have implemented Integration Tests for my .Net Core API using xUnit, shared Class Fixtures, and dependency injection so I can use WebApplicationFactory.

public class DatabaseFixture : WebApplicationFactory<Startup> {
    public DatabaseFixture(WebApplicationFactory<Startup>) {
        //Initialize Stuff
    }
}

public class SaveTests : IClassFixture<DatabaseFixture> {
    private DatabaseFixture databaseFixture;

    public SaveTests(DatabaseFixture databaseFixture)
    {
        this.applicationFixture = applicationFixture;
    }

    //Tests.....
}

public class SaveTests2 : IClassFixture<DatabaseFixture> {
    private DatabaseFixture databaseFixture;

    public SaveTests2(DatabaseFixture databaseFixture)
    {
        this.applicationFixture = applicationFixture;
    }

    //Tests.....
}

This has worked well, but, the Test Data is initialized for each test file.

I am looking to update my tests to use a Collection Fixture instead of a Class Fixture however I have been unable to implement a Collection Fixture while injecting WebApplicationFactory.

[CollectionDefinition("SaveTestCollection")]
public class SaveTestCollectionFixture : ICollectionFixture<DatabaseFixture>
{
}

public class DatabaseFixture : WebApplicationFactory<Startup> {
    public DatabaseFixture(WebApplicationFactory<Startup>) {
        //Initialize Stuff
    }
}

[Collection("SaveTestCollection")]
public class SaveTests
{
    DatabaseFixture fixture;

    public SaveTests(DatabaseFixture fixture)
    {
        this.fixture = fixture;
    }
}

The error I get is:

System.AggregateException : One or more errors occurred. (Collection fixture type 'DatabaseFixture' had one or more unresolved constructor arguments: WebApplicationFactory) (The following constructor parameters did not have matching fixture data: DatabaseFixture fixture)
---- Collection fixture type 'DatabaseFixture' had one or more unresolved constructor arguments: WebApplicationFactory`1 factory
---- The following constructor parameters did not have matching fixture data: DatabaseFixture fixture

I understand that the Dependency Injection is failing, but I am not sure how to fix it.

So my question is, am I able to include the WebApplicationFactory via Dependency Injection, or do I need to change how I am using WebApplicationFactory to work with Collection Fixtures?

Any help is greatly appreciated


Solution

  • Ended up implementing the following solution, which is documented on the XUnit Github page:

    • Continue using IClassFixture
    • Only fully execute the fixture constructor the first time in, and keep a static reference to the fully initialized fixture
    • Any additional instances of the fixture constructor exit early, and do not re-generate the test data
    • All Test files reference the static fixture instead of the fixture passed into the test constructor
    • The fixture still needs to be passed into the test constructor so the fixture constructor is fully executed when it is needed

    Main Collection to provide the WebApplicationFactory

    [CollectionDefinition("Tests")]
    public class FixtureCollection : ICollectionFixture<WebApplicationFactory<Startup>>
    {
    
    }
    
    • Fixture uses the Injected WebApplicationFactory to initalize the test data.
    • There is now a static property that keeps a reference to an instance of the fixture
    • If the static Instance has not been defined, run fixture initialization, and set the static instance to reference this instance of the fixture
    • If the static Instance has been defined, do not run fixture initialization
    • This ensures that the Database data is only generated once, no matter how many times the fixture is used
    public class SaveTestDatabaseFixture : WebApplicationFactory<Startup>{
    
        public static SaveTestDatabaseFixture Instance;
    
        public TestData first_test_data { get; set; }
    
        public SaveTestDatabaseFixture(WebApplicationFactory<Startup> factory)
        {
              if (Instance != null)
              {
                  return;
              }
    
              var context = factory.GetContext();
              ......
              first_test_data = new TestData();
              context.TestData.Add(first_test_data);
              context.SaveChanges();
    
              Instance = this;
        }
    }
    

    All tests use the static instance of the Database Fixture.

    [Collection("Tests")]
    public class TestDataTests : IClassFixture<SaveTestDatabaseFixture> {
        private readonly SaveTestDatabaseFixture databaseFixture;
    
        public TestDataTests (SaveTestDatabaseFixture _)
        {
            this.databaseFixture = SaveTestDatabaseFixture.Instance;
        }
    
        [Fact]
        public async Task Test1()
        {
            var x = databaseFixture.TestData.Id;
            //Run Test
        }
    }
    
    [Collection("Tests")]
    public class TestData2Tests : IClassFixture<SaveTestDatabaseFixture> {
        private readonly SaveTestDatabaseFixture databaseFixture;
    
        public TestData2Tests (SaveTestDatabaseFixture _)
        {
            this.databaseFixture = SaveTestDatabaseFixture.Instance;
        }
    
        [Fact]
        public async Task Test2()
        {
            var x = databaseFixture.TestData2.Id;
            //Run Test
        }
    }
    

    As I have quite a few separate test files, this change reduced the amount of data being saved to the Test Database by 90% (2000 rows down to 200 rows). It also reduced the execution time of the test suite (including Database setup time) from from 53 seconds to 20 seconds.