We are currently migrating NUnit tests over to xUnit. In our previous tests, we had multiple levels of test bases:
public class TestBase {
protected ICacheManagerFactory _cacheManagerFactory;
[OneTimeSetup]
public void OneTimeSetup()
{
//Setup code
}
}
public class ControllerTestBase : TestBase {
[OneTimeSetup]
public void OneTimeSetup()
{
//Setup code
_cacheManagerFactory.SomeSetup();
}
}
We want to replicate that functionality in xUnit such that the original TestBase setup isn't ran multiple times. We are trying to use collections within collections like so:
public sealed class CollectionFixtureBase {
public ICacheManagerFactory CacheManagerFactory;
public CollectionFixtureBase
{
//Setup code
}
}
[Collection("CollectionFixtureBase")]
public sealed class ControllerCollectionFixture {
public CollectionFixtureBase CollectionBase;
public ControllerCollectionFixture(CollectionFixtureBase collectionBase)
{
CollectionBase = collectionBase
CollectionBase.CacheManagerFactory.SomeSetup();
}
}
(I've omitted the collection fixture definitions for brevity)
However, when we run this code we are getting dependency injection errors:
System.AggregateException : One or more errors occurred. Collection fixture type 'Instanda.UnitTests.New.Collections.ControllerCollectionFixture' had one or more unresolved constructor arguments: CollectionFixtureBase collectionFixture The following constructor parameters did not have matching fixture data: ControllerCollectionFixture collectionFixture
Are we going about this the wrong way? We could inherit from the base classes but I'm under the impression this causes the base code to run multiple times? Is there another way to get this functionality?
When trying to understand CollectionFixture
as a newcomer (especially one experienced in NUnit, JUnit and other systems that tend to lead to class hierarchies in ones Test Classes), I'd recommend a good number of passes over https://xunit.net/docs/shared-context ; the model is quite different and subtle. OK, now you're back from there: TL;DR
the main thing xUnit Collection Fixtures achieve is to ensure only one test at a time gets to use the shared thing
xUnit manages them:
a. makes them (once)
b. passes them to your test class via the IUse
* just before each test runs (remember the key diff between NUnit and xUnit is that there's a Fresh Fixture every time
disposes them when no more classes/tests need them
that's it - no class hierarchy etc
While Collection Fixtures are the most powerful construct that's hard to write manually, and relatively common for e.g. wrapping databases, don't forget Class Fixtures - they're the work horses for typical OneTimeSetup
like in the OP
When doing your high level break down of how to manage shared fixtures across Test Classes, the main thing you want to do is let as much stuff run concurrently and independently as possible (xUnit runs multiple Test Classes concurrently by default; Collection Fixtures are the key way to constrain this)
When migrating between the typical class hierarchy you get with the NUnit patterns and OneTimeSetup
/Setup
/Teardown
attributes, general high level advice:
IDisposable.Dispose
to replace Setup and Teardown methods)