Jason
Jason

Reputation: 860

NUnit 3.x - TestCaseSource for descendant test classes

I currently have a set of unit tests which are consistent for a number of Rest API endpoints. Say the class is defined like so.

public abstract class GetAllRouteTests<TModel, TModule>
{
  [Test]
  public void HasModels_ReturnsPagedModel()
  {
     // Implemented test
  }
}

With the implemented test fixture looking like:

[TestFixture(Category = "/api/route-to-test")]
public GetAllTheThings : GetAllRouteTests<TheThing, ModuleTheThings> { }

This enables me to run a number of common tests across all GET all/list routes. It also means that I have classes which are linked directly to the module being tested, and links between tests and code in Resharper / Visual Studio / CI "just work".

The challenge is that some routes require query parameters for testing other pathways through the route code;

e.g. /api/route-to-test?category=big.

As [TestCaseSource] requires a static field, property, or method there appears to be no nice way to override a list of query strings to pass. The closest thing I have come up with seems like a hack. Namely:

public abstract class GetAllRouteTests<TModel, TModule>
{
  [TestCaseSource("StaticToDefineLater")]
  public void HasModels_ReturnsPagedModel(dynamic args)
  {
     // Implemented test
  }
}

[TestFixture(Category = "/api/route-to-test")]
public GetAllTheThings : GetAllRouteTests<TheThing, ModuleTheThings> 
{
    static IEnumerable<dynamic> StaticToDefineLater() 
    {
       // yield return all the query things
    }
}

This works because the static method is defined for the implemented test class, and is found by NUnit. Huge hack. Also problematic for someone else consuming the abstract class as they need to "know" to implement "StaticToDefineLater" as a static something.

I am looking for a better way of achieving this. It seems like non-static TestCaseSource sources were removed in NUnit 3.x, so that's out.

Thanks in advance.

NOTES:

Upvotes: 3

Views: 1537

Answers (1)

j4nw
j4nw

Reputation: 2405

The way I solved a similar problem is by having a base source class that implements IEnumerable (another acceptable source for NUnit), consider if this design suits your usecase:

// in the parent fixture...
public abstract class TestCases : IEnumerable
{
    protected abstract List<List<object>> Cases { get; }

    public IEnumerator GetEnumerator()
    {
        return Cases.GetEnumerator();
    }
}

// in tests
private class TestCasesForTestFoobar : TestCases
{
    protected override List<List<object>> Cases => /* sets of args */
}

[TestCaseSource(typeof(TestCasesForTestFoobar))]
public void TestFoobar(List<object> args)
{
    // implemented test
}

Upvotes: 5

Related Questions