C# Mock(Moq)或重写密封类中的只读属性?

C# Mock(Moq)或重写密封类中的只读属性?,c#,unit-testing,mocking,tdd,moq,C#,Unit Testing,Mocking,Tdd,Moq,在MS Dynamics Crm中,它使用密封类和只读属性返回一些对象(我只能假设使用内部构造函数或内部属性集)。这些人并不是从我可以使用的接口继承的。显然,如果我能控制这段代码,我会有更多的控制权,但因为它在底层的MS Dynamics框架中,我有点卡住了 我想要模拟/重写的类被调用。我之所以要模拟它,是因为我试图模拟对dynamics的调用,并假装返回了一些别名值 这是一个场景,我想创建一个Dynamics将返回的示例实体。。。大概是这样的: var new_entity = new Ent

在MS Dynamics Crm中,它使用密封类和只读属性返回一些对象(我只能假设使用内部构造函数或内部属性集)。这些人并不是从我可以使用的接口继承的。显然,如果我能控制这段代码,我会有更多的控制权,但因为它在底层的MS Dynamics框架中,我有点卡住了

我想要模拟/重写的类被调用。我之所以要模拟它,是因为我试图模拟对dynamics的调用,并假装返回了一些别名值

这是一个场景,我想创建一个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 {*****} }
            }
    };
var crmWrapper = new MSDynamicsCrmWrapper(_myMsDynamicsCrm);
var entityWrapper = crmWrapper.AliasedValue(url);
因此,我引用了一个“联系人”实体(实时返回为AliasedValue)。在测试过程中,我希望确保我的代码处理可能返回到此字段的某些值(例如,它知道如何处理别名值,并且如果没有返回链接联系人,它不会爆炸,等等)

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

接下来,我将创建自己的类并覆盖整个过程。。。但它是密封的

除此之外,正如您可能已经猜到的,Moq不喜欢试图模仿一个密封的类

我已经读到,我需要购买一些“更好的”模拟框架来实现这一点,但我真的不想为了绕开这件事而花费一些额外的钱

有人能给我一个好的解决办法吗

澄清更新


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

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

也许我有点傻,但我不太明白答案(由Lunivore)将如何解决这个问题。。。(添加注释以获得反馈…

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

对于VisualStudio2012,Moles已重命名为Fakes,并将包含在安装中。对于VisualStudio2010,您需要单独下载它。因为它是一个基于分析器的框架,所以您需要使用它们的runner
moles.runner.exe运行测试,这样它就可以设置分析器了

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


另一种选择是按照Lunivore answer的说明添加一个抽象层。我自己更喜欢用工具来解决这个问题,但我必须提前告诉你,使用鼹鼠并不是那么容易。其他基于profiler的模拟框架,如或可能更容易使用,但它们是商业解决方案。

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

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

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 {*****} }
            }
    };
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的范围缩小到实际使用的部分,因此代码可能更易于维护


在计算中,没有任何东西是另一层抽象无法解决的…

在沿着若昂·安杰洛的路线前进之前,我想探讨一下这一点。。。为了澄清,我编辑了原始帖子。你能看看并告诉我你的想法吗?(还有,“clicky”这个词可能有点误导了你!我的意思是“click”这个词,也就是说,它是一个url)好的,所以在阅读你要做的事情时,你需要包装OrganizationService、Entity和EntityCollection以及AliasedValue。它只有四个类,但是一旦你完成了,你就可以在你的代码中的任何地方使用这些类。如果手头有GoF模式手册,请查看适配器模式。然后,每个类都包含其真实的委托对象,或者(对于模拟的AliasedValueWrapper)您正在查找的测试数据。好的,在接受答案之前,让我再次查看适配器模式。(我经常使用这种模式,所以可能我今天只是有一个金发女郎的时刻…)是适配器在类上返回适配器,其中包含适配器,这可能会让你头晕目眩——但实际上,这并没有那么棘手;它们只是包装器。imo“逐检测试”=容易出错。我不惜一切代价避免这样做,因为我一直都在看锥!