如何在单元测试中模拟伪造的 TreeNode 的 Parent 访问器?
How can I mock the Parent accessor of a faked TreeNode in a unit test?
使用 Kentico 12 SP,修补程序 64 - 我可以创建伪造的 TreeNode 类型并在大多数字段上设置值,但我无法设置 return Parent 的值,我需要对方法进行 运行 测试。
我要测试的方法:
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;
}
在单元测试中我可以Fake一个文档类型Folder
DocumentGenerator.RegisterDocumentType<Folder>(Folder.CLASS_NAME);
Fake().DocumentType<Folder>(Folder.CLASS_NAME);
我可以创建实例并为其他属性设置值,它们按预期工作
Folder baseFolder = TreeNode.New<Folder>()
.With(f => f.SetValue("DocumentName", docName))
.With(f => f.SetValue("NodeAliasPath", docPath));
但是当我尝试为“Parent”设置 return 值时,它会在被测试方法调用时忽略该值。
Folder underFolder= TreeNode.New<Folder>()
.With(f => f.SetValue("Parent", baseFolder));
我尝试使用 NSubstitute 更改 Parent underFolder.Parent.Returns(baseFolder);
的 return 值,但它抛出异常 "NSubstitute.Exceptions.CouldNotSetReturnDueToNoLastCallException : Could not find a call to return from."
。
对该错误的搜索似乎表明我没有按照 NSubstitute 预期的方式伪造 class,应该是这样的:var mockFolder = Substitute.For<Folder>();
我也尝试了 Moq 版本,returned 错误 System.TypeLoadException : Method 'DeleteInternal' on type 'Castle.Proxies.FolderProxy' from assembly 'DynamicProxyGenAssembly2...
表示模拟框架无法读取 TreeNode 的一个或多个属性...呃。
无论如何,我应该使用不同的策略来测试它吗?我不想为 TreeNode 编写包装器,但似乎我可能必须对其进行测试?
我认为单元测试不可能做到这一点。
TreeNode.Parent
不是由数据库字段支持的 field/primitive 值(即使 NodeParentID
是),因此使用 .SetValue()
不会有任何效果。
在源代码中,TreeNode.Parent
属性 至少进行了 1 次数据库查询(可能是 2 次),而这些都不是由 Kentico 单元测试基础架构处理的。
我的建议是使用 2 个选项中的 1 个。
- 通过使用抽象从
TreeNode
中获取其值来隔离您对 TreeNode.Parent
的使用,然后使用类似 NSubstitute
的东西(或手动)在您的测试中创建一个存根.
public interface ITreeNodeAccessor
{
TreeNode GetParent(TreeNode node);
}
- 切换到 Integration Test。
当单元测试基础架构无法处理我的用例时,我个人采用了这两种方法 - 我采用的方法取决于我实际尝试测试的内容。
使用 Kentico 12 SP,修补程序 64 - 我可以创建伪造的 TreeNode 类型并在大多数字段上设置值,但我无法设置 return Parent 的值,我需要对方法进行 运行 测试。
我要测试的方法:
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;
}
在单元测试中我可以Fake一个文档类型Folder
DocumentGenerator.RegisterDocumentType<Folder>(Folder.CLASS_NAME);
Fake().DocumentType<Folder>(Folder.CLASS_NAME);
我可以创建实例并为其他属性设置值,它们按预期工作
Folder baseFolder = TreeNode.New<Folder>()
.With(f => f.SetValue("DocumentName", docName))
.With(f => f.SetValue("NodeAliasPath", docPath));
但是当我尝试为“Parent”设置 return 值时,它会在被测试方法调用时忽略该值。
Folder underFolder= TreeNode.New<Folder>()
.With(f => f.SetValue("Parent", baseFolder));
我尝试使用 NSubstitute 更改 Parent underFolder.Parent.Returns(baseFolder);
的 return 值,但它抛出异常 "NSubstitute.Exceptions.CouldNotSetReturnDueToNoLastCallException : Could not find a call to return from."
。
对该错误的搜索似乎表明我没有按照 NSubstitute 预期的方式伪造 class,应该是这样的:var mockFolder = Substitute.For<Folder>();
我也尝试了 Moq 版本,returned 错误 System.TypeLoadException : Method 'DeleteInternal' on type 'Castle.Proxies.FolderProxy' from assembly 'DynamicProxyGenAssembly2...
表示模拟框架无法读取 TreeNode 的一个或多个属性...呃。
无论如何,我应该使用不同的策略来测试它吗?我不想为 TreeNode 编写包装器,但似乎我可能必须对其进行测试?
我认为单元测试不可能做到这一点。
TreeNode.Parent
不是由数据库字段支持的 field/primitive 值(即使 NodeParentID
是),因此使用 .SetValue()
不会有任何效果。
在源代码中,TreeNode.Parent
属性 至少进行了 1 次数据库查询(可能是 2 次),而这些都不是由 Kentico 单元测试基础架构处理的。
我的建议是使用 2 个选项中的 1 个。
- 通过使用抽象从
TreeNode
中获取其值来隔离您对TreeNode.Parent
的使用,然后使用类似NSubstitute
的东西(或手动)在您的测试中创建一个存根.
public interface ITreeNodeAccessor
{
TreeNode GetParent(TreeNode node);
}
- 切换到 Integration Test。
当单元测试基础架构无法处理我的用例时,我个人采用了这两种方法 - 我采用的方法取决于我实际尝试测试的内容。