嘲弄/伪造一个活动的CRM实体

本文关键字:活动 一个 CRM 实体 伪造 嘲弄 | 更新日期: 2023-09-27 17:50:49

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

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类制作一个包装器呢?"

可以通过将我想模拟的每个实体的statecode属性设置为virtual来更改生成的代码,但是当/如果重新生成实体包装器时,这将不会持续存在。这似乎也不是Moq的方式,但我可能是错的。

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

我最有希望的努力是做一个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; } 
    }
}

以及组织服务模拟在调用Retrieve Account时返回活动帐户的特定设置用例:

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);

当我检查服务返回的对象时。检索方法,它几乎完全是我想要的!类型Entity的帐户将statecode设置为我想要的方式,但是当实体转换为Account时,statecode将返回null。这可能是因为转换调用Account构造函数,该构造函数创建一个所有字段为空的Account对象,然后将所有具有公共setter的字段设置为可用值。基本上,当Account是后期绑定时,它是我想要的几乎,当它是早期绑定时,我失去了我设置的状态码值。

// 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!

我的产品代码专门使用早期绑定实体是有充分理由的——主要是使用早期绑定的开发并不糟糕(即智能感知、编译器检查等)。我不想改变生产代码,只是为了更好地与Moq啮合。这和早期绑定的实体非常棒!

我做错了什么与Moq?我是否需要忍气吞声,在生产代码中使用AccountWrapper ?在这种情况下,我是否应该转换为早期绑定,这样我就不会丢失状态码?然后我将不得不更改产品代码,以混合后期和早期绑定……讨厌的东西。我担心这样做,因为包装器给出的想法,你可以设置一个实体的状态码直接通过account.statecode = AccountState.[Active|Inactive],而不是使用SetStateRequest知道它不能,注释解释它不能,但事实上看起来就意味着有人会这样做,并期望它能工作。

嘲弄插件逻辑的整个想法是这样我就不需要联系CRM了!我可以模拟任何我需要使用的实体。当对插件逻辑进行单元测试时,没有理由使用真实数据

橡皮小鸭已经听烦了我…

tl;dr -我可以模拟一个早期绑定的CRM实体的只读状态码,这样我就可以在必要时使用Moq和继承/包装器/接口对各种状态码的实体进行单元测试吗?如果是,怎么做?如果没有,为什么?

嘲弄/伪造一个活动的CRM实体

早绑定实体只是对晚绑定的包装。试试下面的代码。基本上,它设置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);
            }
        } 
    }
}

更新:

我强烈建议使用CRM特定的模拟框架,如XrmUnitTest,对CRM/CDS实体进行单元测试。此外,XrmToolBox中的EarlyBoundGenerator将允许所有属性生成为可编辑的