Search code examples
c#unit-testingxunitkenticonsubstitute

How can I mock the Parent accessor of a faked TreeNode in a unit test?


Using Kentico 12 SP, hotfix 64 - I can create faked TreeNode types and set values on most of the fields, but I can't set a return value for Parent which I need to do to run a test on a method.

The method I'm trying to test:

public Dictionary<string, string> GenerateBreadcrumbs(TreeNode treeNode)
{
    var breadcrumbs = new Dictionary<string, string>();
    if (treeNode != null) {
        var revBreadcrumbs = new Dictionary<string, string>();
        var thisNode = (treeNode.Parent != null && treeNode.Parent.NodeAliasPath != "/") ? treeNode.Parent : null;
        while (thisNode != null) {
            revBreadcrumbs.Add(thisNode.DocumentName, thisNode.NodeAliasPath.ToLowerInvariant());
            thisNode = (thisNode.Parent != null && thisNode.Parent.NodeAliasPath != "/") ? thisNode.Parent : null;
        }
        foreach (var item in revBreadcrumbs.Reverse())
        {
            breadcrumbs.Add(item.Key, item.Value);
        }
    }
    return breadcrumbs;
}

In the unit test I can Fake a document type of type Folder

DocumentGenerator.RegisterDocumentType<Folder>(Folder.CLASS_NAME);
Fake().DocumentType<Folder>(Folder.CLASS_NAME);

I can create instances and set values on other properties and they work as expected

Folder baseFolder = TreeNode.New<Folder>()
    .With(f => f.SetValue("DocumentName", docName))
    .With(f => f.SetValue("NodeAliasPath", docPath));

But when I try to set the return value for "Parent" it ignores that value when called by the method being tested.

Folder underFolder= TreeNode.New<Folder>()
    .With(f => f.SetValue("Parent", baseFolder));

I've tried using NSubstitute to change the return value of Parent underFolder.Parent.Returns(baseFolder); but it throws an exception "NSubstitute.Exceptions.CouldNotSetReturnDueToNoLastCallException : Could not find a call to return from.".

Searches on this error seem to indicate that I did not fake the class in the manner NSubstitute expected, which would be something like this: var mockFolder = Substitute.For<Folder>(); I tried the Moq version too, both returned the error System.TypeLoadException : Method 'DeleteInternal' on type 'Castle.Proxies.FolderProxy' from assembly 'DynamicProxyGenAssembly2... indicating that one or more properties of TreeNode can't be read by the mocking frameworks... ugh.

Anyway, should I be using a different strategy for testing this? I didn't want to have to write a wrapper for TreeNode but seems like I may have to to get this tested?


Solution

  • I don't think this is going to be possible with a Unit Test.

    TreeNode.Parent is not a field/primitive value that is backed by a database field (even though NodeParentID is), so using .SetValue() is not going to have any effect.

    In the source code, the TreeNode.Parent property makes at least 1 database query (potentially 2), and neither of these is handled by the Kentico Unit Test infrastructure.

    My recommendation would be to use 1 of 2 options.

    1. Isolate your use of TreeNode.Parent by using an abstraction to get its value from a TreeNode, and then create a stub in your test using something like NSubstitute (or manually).
    public interface ITreeNodeAccessor
    {
        TreeNode GetParent(TreeNode node);
    }
    
    1. Switch to an Integration Test.

    I've personally taken both approaches when the Unit Test infrastructure fails to handle my use case - the one I take depends on what it is I'm actually trying to test.