0

We've started to introduce some behavior tests that try to test some of out software modules like a complete black box. This test suite was written using inheritance from base test class for easier organization.

Now we'd like to reuse this test suite for the testing of another interface-compatible module. The solution we were able to find was to inherit the test class, and implement another constructor. I'd like to confirm that there's no better option, because writing duplicate inherited classes for each of the test suite class seems wrong.

[TestClass]
public class RouModel_Basic_RunnerBasic : ROUModelTest_Basic
{
    public RouModel_Basic_RunnerBasic() : base()
    {
        //init basic model here
        model = basicModel;
    }
}
[TestClass]
public class RouModel_Basic_RunnerOther : ROUModelTest_Basic
{
    public RouModel_Basic_RunnerOther() : base()
    {
        //init other model here
        model = otherModel;
    }
}

public class ROUModelTest_Basic : RouModelTest
{
   [TestMethod]
   public void TestABC() 
   {
       string input = "abc"
       var result = model.run(input);
       Assert.AreEqual("123", result);
   }
}

public class RouModelTest 
{
    protected IModelTest model;
    ...
}
  • We have used sometime ago by using a base class with virtual members or extra Func for the common test init, tear down etc, which helped isolate common logic to base class. Please clarify if you are trying to reuse code to test business functions or cross cutting concers – Saravanan Mar 29 '20 at 12:10
  • @Saravanan I don't understand your question completely. We use this test suite to verify that module works according to the spec. And want to verify another module compliance to the same spec – YIShikunov Mar 29 '20 at 12:14
  • So the `TestABC` method uses models created in each constructors? – weichch Mar 29 '20 at 12:15
  • @weichch I've clarified my code example to show how model is used in the test suite. – YIShikunov Mar 29 '20 at 12:21
  • In your case, you can have the workflow in the base method, however, do have separate functions so that you can dynamically alter the inputs, write up additional methods using Func properties to test your code to various test cases. Ex: you can have property like `protected Func PreProcessor = null;` Inside your TestABC method you can check if Preprocessor is not null and if so, call that or else continue the base method flow. This gives you more options to customize the code to accomodate multiple scenarios easily. – Saravanan Mar 29 '20 at 12:25
  • I would use test cases: https://www.meziantou.net/mstest-v2-data-tests.htm – weichch Mar 29 '20 at 12:26
  • @weichch I've got the same data but different classes to test. – YIShikunov Mar 29 '20 at 12:43

1 Answers1

1

If you just want to re-use the test code as-is but with a different module under test, inheritance seems to be the most straightforward, since you will need a separate test method for each test, and inheritance is the only way to do that without having to type them yourself. This shouldn't introduce any duplication, since you only have to re-implement the parts that are actually different in each subclass.

If your issue is with the fact that you are building your test fixture in the test case class constructor, an alternative would be to apply the Template Method design pattern to your test methods, and add a virtual creation method for the module under test that subclasses can override to create instances of the specific module you want them to test. Alternatively, you could create a test setup method and mark it with the appropriate attribute, as described in this answer.

That being said, if you really want to keep them all in the same test case class, you might be able to do so if you implement creation methods for the individual modules under test on your base test case class, and then pass the names of those methods to your test methods and call them using reflection. There should be an attribute that allows you to pass arguments to test methods, which is discussed in this answer. However, the feasibility of this approach is just speculation on my part, and you might run the risk of making your tests more obscure.

Vera
  • 644
  • 5
  • 10
  • Thanks for covering all viable options. It seems that inheritance is the most appropriate option after all. – YIShikunov Mar 29 '20 at 12:48
  • @YIShikunov You're welcome. Would you consider giving my answer a vote up and marking it as the accepted answer if it fully answered your question? – Vera Mar 29 '20 at 13:02