模拟(Moq)或覆盖密封类中的只读属性?

时间:2012-08-10 16:27:06

标签: c# unit-testing mocking tdd moq

我有一种情况,在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的形式返回)。在测试时,我想确保我的代码处理可能在此字段中返回的某些值(例如,它知道如何处理别名值,如果没有返回链接的联系人,它不会爆炸,等等)。

因此,如果您点击了AliasedValue的链接,您会发现这些属性是只读的...所以我无法设置测试数据......

接下来,我打算创建自己的课程并覆盖整个事情......但它已经密封......

除此之外,正如你可能已经猜到的那样,Moq并不喜欢试图模仿一个密封的班级。

我已经读过我需要购买一些“更好”的模拟框架才能做到这一点,但我真的不想为了解决这个问题而花费一些额外的钱。

任何人都有一个很好的小解决方案让我绕过这个?

澄清更新


返回的方法示例如下。我有一个服务,将被嘲笑返回上述对象。或者实际返回的是包含上述对象的集合。所以就是这样:

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

这很好,我可以返回上面的实体并用它做我想做的事。所以,在我创建了上面的实体后,我通过我的模拟服务返回它,我在“EntityCollection”中得到了上述实体。一旦我拥有了我的实体,我就按如下方式访问该属性:

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

也许我很傻但我无法得到答案(由Lunivore解决)......(添加评论以获得反馈......)

2 个答案:

答案 0 :(得分:6)

通过添加一个非常简单的适配器类从第三方库中抽象出来,您可以通过检查来测试它。而不是扩展您的目标类,只需将您的新类组合在其上面。它可以具有相同的方法名称,相同的属性等(虽然它不必) - 它只是委托给密封的类。

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

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缩小到您实际使用的位,因此代码可能更容易维护。

计算中没有任何其他抽象层无法解决......

答案 1 :(得分:0)

为了模拟密封类,非虚拟方法或静态成员通常需要一个基于探查器的模拟框架,以便能够拦截所有调用。我所知道的唯一框架就是Microsoft Moles

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

前段时间我写了一篇博文,描述了在Moles下运行NUnit测试的过程,您可以在以下位置查看:

Unit Testing with NUnit and Moles Redux

另一种选择是遵循Lunivore回答的方向并添加一个抽象层。我自己更喜欢用工具来解决这个问题,但我必须事先建议你,Moles不是那么容易使用的。其他基于探测器的模拟框架(如TypeMock IsolatorJustMock)可能更容易使用,但它们是商业解决方案。