8

尝试在 CRM 2011 中调试插件可能非常困难。将 .pdb 文件放在服务器上的正确位置不仅存在问题,而且每次进行编码更改时,都会遇到部署和重新注册插件的麻烦。由于触发器在 CRM 本身中,因此很难为其创建单元测试。

我目前为全新插件编写单元测试的过程相当缓慢且错误,但大致如下:

  1. 使用 SDK 插件注册工具注册新插件
  2. 将调试器附加到 w3wp.exe,在插件代码中放置一个断点。
  3. 通过注册运行的任何操作触发插件。
  4. 当断点被击中时,将管道的原像、后像和目标值序列化为 XML 文件,这将成为我的单元测试的输入。
  5. 停止调试并创建一个新的单元测试,使用 RhinoMocks 模拟 PluginExecutionContext 和 ServiceProvider,使用加载序列化的 XML 文件作为输入参数的存根。
  6. 创建在每个单元测试开始和结束时运行的方法,这些方法会重置(首先尝试删除,然后添加)单元测试要处理的虚拟数据,然后在测试结束时删除虚拟数据。
  7. 编辑序列化文件以引用虚拟数据,以便我可以确保插件在每次运行时都能针对完全相同的数据工作。
  8. 在单元测试中声明并实例化插件,传入模拟对象
  9. 执行插件,运行其他查询以确保插件执行了我期望的工作,断言失败。

这是一件很痛苦的事情。从获得正确的图像到创建虚拟数据并在每次运行测试时重置它,似乎还有很多需要改进的地方。

我如何才能对插件进行单元测试,而不必从 CRM 实际触发它,或者先在 CRM 中调试它,然后为每个测试创建唯一的虚拟数据?如何使用注入来消除在 CRM 中为每个单元测试删除、创建、测试、验证和删除数据的需要?

2016 年更新

这个问题仍然有很多点击,所以我想我会添加两个(我知道的)为单元测试提供假 CRM 实例的开源项目:

  • FakeXrmEasy - 由 Jordi 创建(请参阅下面的答案)
    • 主要是假 CRM 服务
    • 支持插件/工作流伪造
    • 对 FakeItEasy 的依赖
    • 很棒的文档
  • XrmUnitTest -- 我自己创建
    • 假 CRM 服务 + 更多(假设、实体构建器等)
    • 对插件/工作流伪造的流畅支持
    • 不依赖任何模拟框架
    • 糟糕的文档(我正在研究它)

查看我创建的此视频以比较和对比差异。

4

3 回答 3

4

我将插件执行上下文序列化为文件以供单元测试使用。在 codeplex 上有一个很好的项目可以做到这一点http://crm2011plugintest.codeplex.com/

使调试和单元测试更容易,您可以“记录”真实世界的测试。

于 2012-07-27T07:33:27.800 回答
2

我如何才能对插件进行单元测试,而不必从 CRM 实际触发它,或者先在 CRM 中进行调试,然后为每个测试创建唯一的虚拟数据?

带着嘲讽。请参阅此链接以了解使用 RhinoMocks 模拟哪些类。听起来你在这方面正在努力。

如何使用注入来消除在 CRM 中为每个单元测试删除、创建、测试、验证和删除数据的需要?

为输入参数注入值可以通过在您要操作的实体的手动实例中存根来完成:

// Add the target entity     
Entity myStubbedEntity = new Entity("account");
// set properties on myStubbedEntity specific for this test...
ParameterCollection inputParameters = new ParameterCollection();     
inputParameters.Add("Target", myStubbedEntity);     
pipelineContext.Stub(x => x.InputParameters).Return(inputParameters); 

这不是比捕获 xml 数据和重新处理整个输入参数集合更容易吗?


编辑:对于数据访问,通常的建议是将数据访问包装到类中。存储库模式很流行,但对于我们在这里需要的东西来说太过分了。对于您的插件执行类,您在创建时“注入”您的模拟类。初始化默认存储库的空白构造函数,以及采用 IRepository 的第二个构造函数。

public class MyPluginStep
{
    ITaskRepository taskRepository;
    public MyPluginStep(ITaskRepository repo)
    {
        taskRepository = repo;
    }
    public MyPluginStep()
    {
        taskRepository = new DefaultTaskRepositoryImplementation();
    }
    public MyExecuteMethod(mypluginstepparams){
        Task task = taskRepository.GetTaskByContact(...);
    }

根据插件步骤的复杂性,这可能会演变为将许多存储库传递给每个类,并且可能会变得繁重,但这是您可以根据需要添加复杂性的基础知识。

于 2012-07-26T20:57:15.630 回答
2

一个非常好的选择是使用一个模拟库来为你处理模拟和假货,因为我想创建自己的并且总是浪费大量时间来创建假货或模拟,直到我创建了这个为你做的库。试试FakeXrmEasy

于 2016-01-21T15:56:46.730 回答