2014-03-19 23 views
4

我一直在使用Moq来模拟各种CRM早期绑定的实体,所以我可以单元测试我的插件。我想伪造或模拟活动帐户,但问题是statecode是只读而非虚拟的字段。举例来说,当我试图嘲弄Account早期绑定实体指定什么它应该返回如果statecode访问我得到:嘲笑/伪造一个活跃的CRM实体

var accountMock = new Mock<Account>(); 
accountMock.SetupGet(x => x.statecode).Returns(AccountState.Active); 

NotSupportedException抛出:Invalid setup on a non-virtual (overridable in VB) member: x => x.statecode。发生这种情况是因为在SDK提供的Account的早期绑定包装类中,statecode字段不是虚拟的。 Moq不能像我要求的那样重写它!我想,“为什么不为我所拥有的Account类包装?”。

改变由设置我想嘲笑到virtual每个实体的属性statecode生成的代码,但是当/如果实体包装的再生,将不在身边坚持。它似乎也不是Moq的方式,但我可能会误解。

我目前正在使用的回退是读取已从文件中激活的XML序列化帐户,但这实际上违背了嘲笑的目的,因为我基本上有一个要读取的示例数据文件。它有效,但它不嘲笑。

我最有希望的努力是制作一个TestAccount包装器,它扩展了Account并使其看起来像你可以设置以及得到statecode。这是最有希望的,因为我实际上可以嘲笑那个TestAccount类,告诉OrganizationService返回一个有效的statecodestatuscode(它确实!),并确认当它是Entity类型时,它具有正确的字段。当TestAccount实例最终转换为早期绑定的Account类型时,它失败。 statuscode设置卡住了,但statecode设置没有推测是因为没有公共设置者statecode,比如statuscode

我会通过代码解释!

// Wrapper class for Account so I can mock active and inactive Accounts by changing the statecode and statuscode 
public class AccountWrapper : Account 
{ 
    // the member to store our "set statecode" values; only for use in testing and mocking 
    private AccountState? _statecode; 

    // override and replace the base class statecode 
    public new virtual AccountState? statecode 
    { 
     get { return _statecode; } 
     // this is how I intend to get around the read-only of this field in the base class 
     // the wrapper pretends to allow the statecode to be set, when it really does not stick on the actual Account entity 
     set { _statecode = value; } 
    } 
} 

和具体设置情况为组织服务模拟返回一个有效帐户时,检索帐户被称为:

var activeAccountMock = new Mock<AccountWrapper>(); 
activeAccountMock.SetupGet(x => x.statecode).Returns(AccountState.Active); 
var serviceMock = new Mock<IOrganizationService>(); 
serviceMock.Setup(t => 
    t.Retrieve(It.Is<string>(s => s == Account.EntityLogicalName), 
       It.IsAny<Guid>(), // don't care about a specific Account 
       It.IsAny<ColumnSet>())) // don't care about a specific ColumnSet 
    .Returns(activeAccountMock.Object); 

当我检查由service.Retrieve方法返回的对象,它几乎正是我想要的! Entity类型的帐户已设置我想要的方式,但当实体转换为Account时,statecode返回为空。这可能是因为转换调用Account构造函数,该构造函数创建一个所有字段都为null的Account对象,然后设置具有可用值的公共setter的所有字段。基本上,当账户迟到时,我想要差不多,并且当它被早期绑定时,我失去了我设置的状态码值。

// this guy is type Entity, and its statecode is what I want! 
var accountLateBound = service.Retrieve(Account.EntityLogicalName, accountId, new ColumnSet(true)); 
// accountLateBound["statecode"] is AccountState.Active YAY! 
// this clobbers my glorious statecode mock... 
Account accountEarlyBound = accountLateBound.ToEntity<Account>(); 
// accountEarlyBound.statecode is null BOO! 

我的生产代码使用早期绑定实体完全有很好的理由 - 主要是发展与早期绑定不吸(塔尔是intellissense,编译器检查等)。我不想改变生产代码,所以它与Moq的网格更好。那个和早期的实体真棒!

我在做Moq的问题吗?我是否需要吸取它并在生产代码中使用AccountWrapper?我应该不是在这种情况下转换为早期绑定,所以我不会失去状态码?然后,我将不得不改变产品代码,以便混合迟到和早期绑定......恶作剧。我很担心这样做,因为包装器发布了一个想法,即您可以直接通过account.statecode = AccountState.[Active|Inactive]而不是使用SetStateRequest来设置实体的状态码。 知道它没有,评论解释它不,但它的事实看起来像像你可以意味着有人会做到这一点,并期望它的工作。

嘲笑插件逻辑的整个想法是,我根本不需要联系客户关系管理的任何东西!我可以嘲笑任何我需要使用的实体。当单元测试插件的逻辑,没有理由使用真实的数据

的橡皮鸭已经病了听我的......

TL;博士 - 我可以模拟只读statecode一个早期绑定的CRM实体,所以我可以使用Moq和继承/包装/接口进行单元测试(如有必要),使用各种状态代码的实体?如果是,如何?如果不是,为什么?

+0

tl; dr我不能说你用这种方式更新或创建记录的特殊方法,但我的模糊大脑深处,我似乎想起你必须(或者也许它应该)总是设置同时状态码和状态码都是状态码,因为状态码依赖于状态码(有效地作为子类别),所以通过一起更新可以避免它们错位的问题 – AdamV

+0

有趣...我会研究这一点。我_think_早期绑定的实体包装只有一个构造函数,并且不包含任何参数。 – gfritz

回答

4

早期绑定实体只是一个围绕晚期边界的包装。尝试下面的代码。基本上,它在Attributes集合中设置值,这是基本账户实际读取的值。如果你尝试在CRM中更新或创建它,它会发生炸弹,但如果一切都是本地的,它应该工作得很好。

// Wrapper class for Account so I can mock active and inactive Accounts by changing the statecode and statuscode 
public class AccountWrapper : Account 
{ 
    // the member to store our "set statecode" values; only for use in testing and mocking 
    private AccountState? _statecode; 

    // override and replace the base class statecode 
    public new virtual AccountState? statecode 
    { 
     get { return _statecode; } 
     // this is to get around the read-only of this field in the base class 
     // the wrapper pretends to allow the statecode to be set, when it really does not stick on the actual Account entity 
     set 
     { 
      _statecode = value; 
      if(value == null){ 
       if(this.Attributes.Contains("statecode")){ 
        this.Attributes.Remove("statecode") 
       } 
      } 
      else 
      { 
       this.SetAttributeValue("statecode", _statecode); 
      } 
     } 
    } 
} 
+0

我看到这个包装和我发布的不同。我会尝试这一个! – gfritz

+0

就是这样!使用属性集合! – gfritz

+0

我已经创建了一个广泛的XRM测试框架,仍然需要添加更多的文档,但是如果你感兴趣:https://github.com/daryllabar/XrmUnitTest – Daryl