Search code examples
c#unit-testingmspec

Does MSpec support "row tests" or data-driven tests, like NUnit TestCase?


We are using Machine.Specification as our test framework on my current project. This works well for most of what we are testing. However, we have a number of view models where we have 'formatted' properties that take some raw data, apply some logic, and return a formatted version of that data.

Since there is logic involved in the formatting (null checks, special cases for zero, etc.) I want test a number of possible data values including boundary conditions. To me, this doesn't feel like the right use case for MSpec and that we should drop down into something like NUnit where I can write a data-driven test using something like the [TestCase] attribute.

Is there a clean, simple way to write this kind of test in MSpec, or am I right in my feeling that we should be using a different tool for this kind of test?

View Model

public class DwellingInformation
{        
    public DateTime? PurchaseDate { get; set; }
    public string PurchaseDateFormatted
    {
        if(PurchaseDate == null)
            return "N/A";

        return PurchaseDate.Value.ToShortDateString();
    }

    public int? ReplacementCost { get; set; }
    public string ReplacementCostFormatted
    {
        if(ReplacementCost == null)
            return "N/A";

        if(ReplacementCost == 0)
            return "Not Set";

        return ReplacementCost.ToString("C0");
    }

    // ... and so on...
}

MSpec Tests

public class When_ReplacementCost_is_null
{
    private static DwellingInformation information;

    Establish context = () =>
    {
        information = new DwellingInformation { ReplacementCost = null };
    };

    It ReplacementCostFormatted_should_be_Not_Available = () => information.ReplacementCostFormatted.ShouldEqual("N/A");
}

public class When_ReplacementCost_is_zero
{
    private static DwellingInformation information;

    Establish context = () =>
    {
        information = new DwellingInformation { ReplacementCost = "0" };
    };

    It ReplacementCostFormatted_should_be_Not_Set = () => information.ReplacementCostFormatted.ShouldEqual("Not Set");
}

public class When_ReplacementCost_is_a_non_zero_value
{
    private static DwellingInformation information;

    Establish context = () =>
    {
        information = new DwellingInformation { ReplacementCost = 200000 };
    };

    It ReplacementCostFormatted_should_be_formatted_as_currency = () => information.ReplacementCostFormatted.ShouldEqual("$200,000");
}

NUnit w/TestCase

[TestCase(null, "N/A")]
[TestCase(0, "Not Set")]
[TestCase(200000, "$200,000")]
public void ReplacementCostFormatted_Correctly_Formats_Values(int? inputVal, string expectedVal)
{
    var information = new DwellingInformation { ReplacementCost = inputVal };
    information.ReplacementCostFormatted.ShouldEqual(expectedVal);
}

Is there a better way to write the MSpec tests that I'm missing because I'm just not familiar enough with MSpec yet, or is MSpec really just the wrong tool for the job in this case?

NOTE: Another Dev on the team feels we should write all of our tests in MSpec because he doesn't want to introduce multiple testing frameworks into the project. While I understand his point, I want to make sure we are using the right tool for the right job, so if MSpec is not the right tool, I'm looking for points I can use to argue the case for introducing another framework.


Solution

  • Short answer, use NUnit or xunit. Combinatorial testing is not the sweet spot of mspec and likely will never be. I never cared for multiple test frameworks in my projects, especially when a second tool works better for specific scenarios. Mspec works best for behavioral specifications. Testing input variants is not.