Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Skip test method for specific type in generic test class

I'm trying to unit test a part of a project; I'm using NUnit. The targeted unit processes objects of several types, all extending a base type. I've created a generic test class on which I set the desired test types:

[TestFixture(typeof(SomeType))]
[TestFixture(typeof(SomeOtherType))]
class MyTestClass<T> where T : SomeBaseType, new()
{

     [Test]
     public void DoThisTest()
     {
         var sut = CreateSut();
         var target = CreateTarget();

         Assert.IsTrue(sut.Process(target));
     }  

     [Test]
     public void DoThatTest()
     {
         var sut = CreateSut();
         var target = CreateInvalidTarget();

         Assert.IsFalse(sut.IsValid(target));
     }  

     //...
}

This creates a set of all the tests for each type set using TestFixture. For whatever reason, I have a test which only makes sense in the context of a specific type. This means that I need to either 1) use Assert.Ignore() on all other types or 2) create a different test class just for those "special" test cases.

Is there a way of opting out from a test from outside (attribute?) and specify that that particular test must not be "implemented" in certain contexts? I would like to "combine" 1) & 2) such that all the test cases are in the same file/class but some tests are only rendered/implemented/run for certain values set by TestFixture.

like image 803
Andrei V Avatar asked Feb 03 '26 19:02

Andrei V


1 Answers

This isn't exactly what you're looking for, but I think it's a pretty close work around. You can specify nested classes within your main test fixture and decorate them with different TestFixture attributes to restrict what's run. It's probably best explained with an example. So, given these data types:

public interface ICompetitor {
    string GetFinalPosition();
}


public class Winner : ICompetitor{
    public string GetFinalPosition() {
        return "Won";
    }
}

public class Loser : ICompetitor {
    public string GetFinalPosition() {
        return "Lost";
    }
}

I can define these TestFixtures:

[TestFixture(typeof(Winner))]
[TestFixture(typeof(Loser))]
public class CompetitorTests<T> where T : ICompetitor, new()
{
    static private T CreateSut() {
        return new T();
    }

    [Test]
    public void EverybodyHasPosition() {
        Assert.IsNotNullOrEmpty(CreateSut().GetFinalPosition());
    }

    [TestFixture(typeof(Winner))]
    public class WinnerTests {
        [Test]
        public void TestWon() {
            Assert.AreEqual("Won", CompetitorTests<T>.CreateSut().GetFinalPosition());
        }
    }

    [TestFixture(typeof(Loser))]
    public class LoserTests {
        [Test]
        public void TestLost() {
            Assert.AreEqual("Lost", CompetitorTests<T>.CreateSut().GetFinalPosition());
        }
    }
}

The EverybodyHasPosition test is run twice (once for the Winner and once for the Loser classes). Whereas the TestWon is only run for the Winner class and the TestLost is only run for the Loser class. It's not ideal, because you can only access static members of the outer class, and each fixture is responsible for it's own setup/teardown.

You can work around this though, by using a base class. So, the state sharing version might look more like this (notice that each TestFixture inherits from CompetitorTestsState):

public class CompetitorTestsState<T> where T : ICompetitor, new()  {
    protected T SUT { get; private set; }

    [SetUp]
    public void Setup() {
        SUT = CreateSut();
    }

    private T CreateSut() {
        return new T();
    }
}

[TestFixture(typeof(Winner))]
[TestFixture(typeof(Loser))]
public class CompetitorTests<T> : CompetitorTestsState<T> where T : ICompetitor, new() {
    [Test]
    public void EverybodyHasPosition() {
        Assert.IsNotNullOrEmpty(SUT.GetFinalPosition());
    }

    [TestFixture(typeof(Winner))]
    public class WinnerTests : CompetitorTestsState<T>{
        [Test]
        public void TestWon() {
            Assert.AreEqual("Won", SUT.GetFinalPosition());
        }
    }

    [TestFixture(typeof(Loser))]
    public class LoserTests : CompetitorTestsState<T>{
        [Test]
        public void TestLost() {
            Assert.AreEqual("Lost", SUT.GetFinalPosition());
        }
    }
}
like image 111
forsvarir Avatar answered Feb 05 '26 07:02

forsvarir