我一直在使用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返回一个有效的statecode
和statuscode
(它确实!),并确认当它是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和继承/包装/接口进行单元测试(如有必要),使用各种状态代码的实体?如果是,如何?如果不是,为什么?
tl; dr我不能说你用这种方式更新或创建记录的特殊方法,但我的模糊大脑深处,我似乎想起你必须(或者也许它应该)总是设置同时状态码和状态码都是状态码,因为状态码依赖于状态码(有效地作为子类别),所以通过一起更新可以避免它们错位的问题 – AdamV
有趣...我会研究这一点。我_think_早期绑定的实体包装只有一个构造函数,并且不包含任何参数。 – gfritz