模拟(最小起订量)或覆盖密封类中的只读属性



我遇到一种情况,在MS Dynamics CRM中,它使用密封类和只读属性返回一些对象(我只能假设使用内部构造函数或内部属性集)。而且这些家伙不会从我可以使用的界面继承。显然,如果我能控制这段代码,我会有更多的控制权,但由于它位于底层 MS Dynamics 框架中,所以我有点卡住了。

我想模拟/覆盖的类称为AliasedValue。我想嘲笑它的原因是我正在尝试模拟对动态的调用,并想假装我得到了一些别名值。

所以这是场景,我想创建一个 Dynamics 将返回的示例实体......像这样:

var new_entity = new Entity("new_entity")
    {
        Attributes = new AttributeCollection
            {
                {"new_name", "BR"},
                {"createdon", DateTime.Now.AddDays(-1).Date},
                {"new_field", "My field"},
                {"new_contact", new AliasedValue {*****} }
            }
    };

所以我引用了一个"联系人"实体(实时返回为别名值)。在测试时,我想确保我的代码处理可能在此字段中返回的某些值(例如,它知道如何处理别名值,并且如果没有返回链接的联系人,它不会爆炸,等等)。

因此,如果您单击了指向 AliasedValue 的链接,您会注意到这些属性是只读的......所以我无法设置测试数据...

接下来,我将创建自己的类并覆盖整个内容......但它是密封的...

最重要的是,正如您可能已经猜到的那样,Moq 不喜欢试图嘲笑密封类。

我已经读到我需要购买一些"更好"的模拟框架来做到这一点,但我真的不想为了解决这 1 件事而花一些额外的钱。

有人为我提供了一个很好的小解决方案来解决这个问题吗?

更新澄清


如何返回的示例如下所示。我有一个服务,它将被模拟以返回上述对象。或者它实际返回的是包含上述对象的集合。所以这是这样的:

var service = new Mock<IOrganizationService>();
service
    .Setup(s => s.RetrieveMultiple(null))
    .Returns(new EntityCollection (new List<Entity> {new_entity}));

这太好了,我可以返回上述实体并用它做我想做的事。因此,在我创建了上述实体并通过我的模拟服务返回它之后,我在我的"实体集合"中得到了上述内容。一旦我有了我的实体,我就会按如下方式访问该属性:

var aliasedContact = (AliasedValue)new_entity.Attributes["new_contact"];

也许我很傻,但我不太明白答案(Lunivore)将如何解决这个问题......(添加评论以获取反馈...

通过添加一个非常简单的适配器类,从第三方库中抽象出来,您可以通过检查对其进行测试。与其扩展目标类,不如在其上编写新类。它可以具有相同的方法名称,相同的属性等(尽管它不必如此) - 它只是委托给密封类。

如果你有一个类返回你的密封类,把它们都包装起来。所以它可能看起来像这样:

var crmWrapper = new MSDynamicsCrmWrapper(_myMsDynamicsCrm);
var entityWrapper = crmWrapper.AliasedValue(url);

和 crmWrapper 将做:

public EntityWrapper AliasedValue(Url url) 
{
    var entity = delegateCrm.AliasedValue(url);
    return new EntityWrapper(entity);
}

简单。您只需要委派您实际使用的方法。

这不仅允许您模拟类,还可以控制下一个版本的MS Dynamics是否稍微更改了API,或者结果是存在奇怪的行为或性能不佳。此外,您将 API 缩小到您实际使用的位,因此代码甚至可能更容易维护。

在计算中,没有什么是另一个抽象层无法解决的......

为了模拟密封类,非虚拟方法或静态成员通常需要一个基于分析器的模拟框架,以便能够拦截所有调用。据我所知,唯一可以做到这一点且非商业性的框架是Microsoft Moles。

对于Visual Studio 2012,Moles已重命名为Fakes,并将包含在安装中。对于Visual Studio 2010,您需要单独下载它。由于它是一个基于探查器的框架,因此您需要使用其运行器moles.runner.exe运行测试,以便它可以设置探查器。

不久前,我写了一篇博客文章,描述了在Moles下运行NUnit测试的过程,您可以在以下位置进行检查:

使用 NUnit 和 Moles Redux 进行单元测试

另一种选择是按照 Lunivore 答案的指示并添加一个抽象层。我自己更喜欢用工具解决这个问题,但我必须预先告诉你,Moles 不是那么容易使用。其他基于分析器的模拟框架,如TypeMock Solator或JustMock可能更容易使用,但它们是商业解决方案。

最新更新