2012-08-10 59 views
3

我有一种情况,在MS Dynamics Crm中它使用密封类和只读属性(我只能假定使用内部构造函数或内部属性集)返回一些对象。而这些人不会从我可以使用的界面继承。很明显,如果我能控制这段代码,我会有更多的控制权,但是因为它在底层的MS Dynamics框架中,所以我有点卡住了。模拟(Moq)或覆盖密封类中的只读操作?

我想要模拟/覆盖的类叫做AliasedValue。我想嘲笑它的原因是我试图模拟对动态的调用,并想假装我得到一些别名的价值。

所以这里的情景,我想创建一个动态将返回一个样本实体......是这样的:

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)将如何解决这个...(添加评论以获得反馈...)

回答

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实际使用的范围,因此代码甚至可能更容易维护。

没有什么在计算那抽象的另一层不会解决......

+0

我想探索gonig下来若昂安杰洛路线在此之前有点...我已经编辑了原来的职位澄清。你能看看并告诉我你的想法吗? (另外,“clicky”这个词可能误导了你一个点!我的意思是它是“点击”这个词,即它是一个URL) – 2012-08-10 16:55:34

+0

好的,所以阅读你想要做的事情,你需要包装OrganizationService ,Entity和EntityCollection和AliasedValue。它只有四个类,但是一旦你完成了它,你就可以在代码中的任何地方使用这些类。如果您有方便的GoF模式手册,请查看适配器模式。然后每个类都包含其真实的委托对象,或者(在模拟AliasedValueWrapper的情况下)您正在查找的测试数据。 – Lunivore 2012-08-10 17:04:21

+0

好吧,让我在接受答案之前再次查看适配器模式。 (我经常使用这种模式,所以也许我今天只是有一个金发的时刻...) – 2012-08-10 17:07:31

0

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

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

前阵子我写了一篇博客文章中描述的痣下运行NUnit的测试过程中,你可以检查一下:

Unit Testing with NUnit and Moles Redux

另一种方法是遵循Lunivore答案的方向,并添加一个抽象层。我自己更喜欢用工具来解决这个问题,但我必须提前告诉你,痣并不是那么容易处理。其他基于Profiler的模拟框架(如TypeMock IsolatorJustMock)可能更易于使用,但它们是商业解决方案。