Search code examples
unit-testinglanguage-agnostictddcode-organization

Scenario-Based Unit Testing: Organization


A friend and I recently came across the idea of scenario-based unit testing, which we think is a great alternative to the test-case-per-class methodology. It's especially attractive because it helps consolidate much of our setup code and reduces the time it takes to create tests. But creating a new test fixture for each scenario gives us a new problem: How should we organize these scenario test fixtures?

If we create a new test fixture for each scenario, we will be creating a lot of fixtures. Where do we put them? In one single mammoth of a file? Or should we create a new file for each scenario, and place that file in a folder with all the other test fixtures?

The reason I ask is because there is great potential for creating a big headache for ourselves when we have to maintain our code later, but we won't be able to tell it's a headache until after the damage has been done.


Solution

  • Scenario-based testing is great and there are certainly a lot of options on how to organize your tests.

    Nested Classes

    A key take away from this style of testing is that we're actually using nested classes to represent our scenarios. We create a root class that represents the container for all the tests and then each scenario is a derived class.

    [TestFixture]
    public class MyClassSpecs
    {
        // common helper methods here
    
        // additional specs/scenarios here...
        [TestFixture]
        public class WhenTheViewIsLoaded : MyClassSpecs
        {
            [Test]
            public void EnsureThatTheButtonIsEnabled()
            {
               /* etc */
            }
        }
    }
    

    Within NUnit, the subclasses are flattened out and the root and nested class name appear as part of the Fixture name:

    MyClassSpecs+WhenTheViewIsLoaded
       > EnsureThatTheButtonIsEnabled
    

    In my projects, I typically create one class file per subject and keep all the scenarios as nested classes. I use Visual Studio's outlining to collapse the nested classes. In this fashion, I can see all the scenarios rather quickly.

    File Per Scenario

    One of the interesting points in Dave's post is that he's using partial classes. He's done so because it makes navigating within Resharper easier, but it also means that fixtures can broken down into different files.

    MyClassSpecs.Scenario1.cs
    MyClassSpecs.Scenario2.cs
    

    Splitting the fixtures into multiple files may be a matter of taste, but it's a very interesting idea. One area where I could see this falling apart is the naming convention used for the files, especially if the scenario names are long or need to change.

    Personally, I would only split into multiple files if it warranted it.

    File Per Testing Strategy

    While I'm personally not in favor of splitting my fixtures into multiple files, one area I can see this making a lot of sense is if you have different testing strategies. For example, consider two testing strategies where one set of unit tests use Mock objects as dependencies and another set using concrete dependencies.

    MySpecs.UsingMocks.cs
    MySpecs.Integration.cs
    

    A word of advise on using subclasses for test fixtures -- readability of these tests is the main concern -- don't get carried away with abstracting the details away from the test, and don't create test fixtures that use several levels of inheritance and you'll be fine.