【问题标题】:How can I mock the Parent accessor of a faked TreeNode in a unit test?如何在单元测试中模拟伪造 TreeNode 的 Parent 访问器?
【发布时间】:2023-04-07 06:49:01
【问题描述】:

使用 Kentico 12 SP,修补程序 64 - 我可以创建伪造的 TreeNode 类型并在大多数字段上设置值,但我无法为 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;
}

在单元测试中,我可以伪造类型为 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”设置返回值时,它会在被测试的方法调用时忽略该值。

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

我尝试使用 NSubstitute 来更改 Parent underFolder.Parent.Returns(baseFolder); 的返回值,但它会引发异常 "NSubstitute.Exceptions.CouldNotSetReturnDueToNoLastCallException : Could not find a call to return from."

对此错误的搜索似乎表明我没有按照 NSubstitute 预期的方式伪造课程,这将是这样的:var mockFolder = Substitute.For&lt;Folder&gt;(); 我也尝试了最小起订量版本,两者都返回了错误 System.TypeLoadException : Method 'DeleteInternal' on type 'Castle.Proxies.FolderProxy' from assembly 'DynamicProxyGenAssembly2...,表明一个模拟框架无法读取 TreeNode 的或更多属性...呃。

无论如何,我应该使用不同的策略来测试它吗?我不想为 TreeNode 编写一个包装器,但似乎我可能需要对其进行测试?

【问题讨论】:

  • 是否可以使用TreeNode 实例进行测试?由于与这些类的交互程度,我认为这将使测试更加可靠。
  • 谢谢@DavidTchepak - 是的,但是将类型更改为TreeNode 并不会改变行为,因为Folder 实现了TreeNode。父母的签名是public virtual TreeNode Parent { get; },我应该使用不同的方法来模拟这个属性吗?
  • 我的意思是使用真正的类而不是模拟。 API 看起来很大,这可能会使模拟出现问题。根据您看到的错误,尝试将NSubstitute.Analyzers 添加到测试项目中,看看它是否检测到像internal member use 这样的问题。

标签: c# unit-testing xunit kentico nsubstitute


【解决方案1】:

我认为这在单元测试中是不可能的。

TreeNode.Parent 不是由数据库字段支持的字段/原始值(即使 NodeParentID 是),因此使用 .SetValue() 不会有任何效果。

在源代码中,TreeNode.Parent 属性至少进行 1 次数据库查询(可能为 2 次),而这些都不由 Kentico 单元测试基础架构处理。

我的建议是使用 2 个选项中的 1 个。

  1. 通过使用抽象从TreeNode 获取其值来隔离您对TreeNode.Parent 的使用,然后使用NSubstitute 之类的东西(或手动)在您的测试中创建一个存根。
public interface ITreeNodeAccessor
{
    TreeNode GetParent(TreeNode node);
}
  1. 切换到Integration Test

当单元测试基础架构无法处理我的用例时,我个人采用了这两种方法 - 我采用的方法取决于我实际尝试测试的是什么。

【讨论】:

  • 这是我用来进行测试的解决方案,谢谢。在这种情况下,我只测试逻辑,因此我认为单元测试更合适。我正在考虑为这个特定场景编写一个包装器,但是添加一个接口IParentAccessorTreeNode GetParent(TreeNode node); 并让部分Folder 类实现它最终成为一个很好的解决方案,几乎不需要重构。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-28
  • 1970-01-01
  • 2012-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多