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?
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.
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);
}
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.