模拟/伪造活跃的CRM实体

问题描述 投票:4回答:1

我一直在使用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序列化帐户,但这实际上违背了模拟的目的,因为我基本上有一个要读取的样本数据文件。它有效,但它不是嘲弄。

我最有希望的努力是制作一个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类型的帐户有statecode设置我想要的方式,但实体转换为Account的那一刻,statecode回到null。这可能是因为转换调用了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来设置实体的状态代码。我知道它没有,评论解释它没有,但事实上,它看起来你可以意味着有人会这样做,并期望它的工作。

模拟插件逻辑的整个想法是,所以我根本不需要联系CRM!我可以模拟我需要使用的任何实体。单元测试插件逻辑时,没有理由使用实际数据

橡皮鸭很难听我说话......

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

c# unit-testing dynamics-crm-2011 moq
1个回答
5
投票

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

更新:

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

© www.soinside.com 2019 - 2024. All rights reserved.