Sunday, October 21, 2018

Unit testing Kentico with xUnit

With Kentico EMS 12 almost ready I noticed that support for MSTest has been dropped from the CMS.Test assembly. I think that’s a good thing given the (slow but steady) move towards .NET Core, where MSTest is no longer the default unit testing framework. I was sort of hoping for a bit more bold move to drop the dependency on a specific test framework all together, or to factor that dependency out to a separate Nuget package.
This is mostly because I really prefer xUnit over NUnit. It’s low ceremony approach to unit testing results in simple and clean code.
Luckily, there’s nothing stopping us from using xUnit with Kentico and at TrueLime we’ve been doing that for over 2 years now. I’ll get to the code shortly but first, it’s probably good to touch on the differences between NUnit and xUnit.
pexels-photo-1331975

Differences between NUnit and xUnit

In a nutshell, xUnit lacks most of the ceremony of older frameworks like NUnit:
  • There’s no Setup or TearDown – use constructor and IDisposable instead
  • Testclasses are not fixtures, Fixtures are in seperate classes to promote reuse
  • Each test runs in it’s own instance of the test class to improve isolation
  • Tests either pass or fail, there is no intermediate state
The net result is that xUnit tests mostly look like the rest of your code. This encourages developers to treat the test code with the same hygene as the application code (refactor, clean up etc.). It also makes it more natural to write clean tests. All the code involved in the test should go into the test, not into setup and teardown at any level.
If you do need to handle some sort of context around your test, like running Kentico, there’s always standard C# constructors and you can implement IDisposable to clean stuff up.’

Enough talk, time for code

Kentico provides support for working with it’s data APIs in unit tests, which is pretty cool. There are some caveats (see next section), but once you’re past those it works quite well and fast.
Unfortunately, since Kentico is based on NUnit we do need to handle some ceremony but we can tuck that away into a base class and keep it out of our test code.
public abstract class KenticoUnitTest : CMS.Tests.UnitTests, IDisposable
{
    protected KenticoUnitTest()
    {
        // Initialize Kentico test infrastructure
        InitFixtureBase();
        InitBase();
        UnitTestsSetUp(); // enable Kentico object faking
    }

    void IDisposable.Dispose()
    {
 // Cleanup Kentico Test infra
        CleanUpTestClass();
        ResetAllFakes();
        try
        {
            CleanUpBase();
            CleanUpFixtureBase();
        }
        catch( System.IO.PathTooLongException )
        {
            // this fails under VS Live testing but that is not critical
        }
    }
}

Kentico Unit Testing Caveats

  • Always use .WithData with Fake if you’re going to query that data. If not, you’re in for some very nasty and hard to decypher stack traces. For example:
    Fake<SettingsKeyInfo,SettingsKeyInfoProvider>().WithData();
  • If you do run into nasty stack traces, especially the ones that end in a failing DB connection, carefully read the first calls in the stack trace and try to figure out what entity is being used so you can fake it.
  • Be careful with VS Live unit testing. We’ve seen some tests failing due to errors unrelated to the test itself.
  • When using nCrunch for continuous testing,make sure you configure the project to copy in referenced assemblies. This is due  Kentico dynamically loading lots of assemblies while scanning for extensions like modules and custom data classes.
  • Custom data classes and other CMS extensions will only be available if the containing assembly is marked with the assembly discoverable attribute:
  • [assembly:CMS.AssemblyDiscoverable]

References