简体   繁体   English

如何为CRM 2011插件正确设置C#单元测试?

[英]How do I correctly setup C# unit tests for CRM 2011 plugins?

Trying to debug a plugin in CRM 2011 can be extremely difficult. 尝试在CRM 2011中调试插件非常困难。 Not only are there issues with having the .pdb files in the correct location on the server, but each time you make a coding change you get to go through the hassle of deploying and re-registering the plugin. 将.pdb文件放在服务器上的正确位置不仅存在问题,而且每次进行编码更改时,都会遇到部署和重新注册插件的麻烦。 Since the trigger is in CRM itself, it's hard to create a unit test for it. 由于触发器是在CRM本身,因此很难为它创建单元测试。

My current process of writing a unit test for a brand new plugin is rather slow and error, but goes something like this: 我目前为一个全新插件编写单元测试的过程相当缓慢且错误,但是这样的事情:

  1. Register the new plugin using the SDK plugin registration tool 使用SDK插件注册工具注册新插件
  2. Attach a debugger to the w3wp.exe, putting a break point in the plugin code. 将调试器附加到w3wp.exe,在插件代码中添加一个断点。
  3. Trigger the plugin through whatever action it is registered to run for. 通过注册运行的任何操作触发插件。
  4. When the break point gets hit, serialize the preimage, postimage, and target values of the pipeline to XML files, this then becomes the input to my unit test. 当断点被击中时,将管道的preimage,postimage和目标值序列化为XML文件,然后这将成为我的单元测试的输入。
  5. Stop debugging and create a new unit test, using RhinoMocks to mock the PluginExecutionContext and ServiceProvider, using loading the serialized XML files as stubs for the input parameters. 停止调试并创建一个新的单元测试,使用RhinoMocks模拟PluginExecutionContext和ServiceProvider,使用序列化的XML文件作为输入参数的存根。
  6. Create methods that get run at the start and end of each unit test that resets (first attempting to delete, then add) dummy data for the unit test to process, then deletes the dummy data at the end of the test. 创建在每个单元测试的开始和结束时运行的方法,这些方法重置(首先尝试删除,然后添加)要处理的单元测试的虚拟数据,然后在测试结束时删除虚拟数据。
  7. Edit the Serialized files to reference the dummy data so that I can ensure that the plugin will work against the exact same data each time it is ran. 编辑序列化文件以引用虚拟数据,以便我可以确保插件在每次运行时都能对抗完全相同的数据。
  8. Declare and instantiate the plugin in the unit test, passing in the mocked objects 在单元测试中声明并实例化插件,传入模拟对象
  9. Execute the plugin, running additional queries to ensure that the plugin performed the work I was expecting, Asserting on failure. 执行插件,运行其他查询以确保插件执行我期望的工作,断言失败。

This is a pain to do. 这是一件痛苦的事。 From getting the images correct, to creating the dummy data, and resetting it each time the test is run, there seems to be a lot of area for improvement. 从获取图像正确,到创建虚拟数据,并在每次测试运行时重置它,似乎有很多需要改进的地方。

How can I unit test a plugin without having to actually trigger it from CRM, or run through all the hoopla of debugging it in CRM first, and creating unique dummy data for each test? 如何在不必从CRM实际触发插件的情况下对插件进行单元测试,或者首先运行在CRM中调试它的所有箍,并为每个测试创建独特的虚拟数据? How can I use injection to eliminate the need to be deleting, creating, testing, verifying, and deleting data in CRM for each unit test? 如何使用注入来消除在CRM中为每个单元测试删除,创建,测试,验证和删除数据的需要?

Update 2016 2016年更新

This question is still getting quite a few hits, so I thought I'd add what the two (that I know of) open source projects that provide Fake CRM instances for unit testing: 这个问题仍然有很多点击,所以我想我会添加两个(我所知道的)开源项目,为单元测试提供Fake CRM实例:

  • FakeXrmEasy -- Created by Jordi (see answer below) FakeXrmEasy - 由Jordi创建(见下面的答案)
    • Primarily Fake CRM Service 主要是伪造的CRM服务
    • Support for Plugin/Workflow Faking 支持插件/工作流程伪造
    • Dependency on FakeItEasy 对FakeItEasy的依赖
    • Great Documentation 伟大的文档
  • XrmUnitTest -- Created by myself XrmUnitTest - 由我自己创建
    • Fake CRM Service + more (Assumptions, Entity Builders, etc) 假CRM服务+更多(假设,实体建设者等)
    • Fluent Support for Plugin/Workflow Faking 流畅支持插件/工作流程伪造
    • No Dependency on any mocking framework 没有对任何模拟框架的依赖
    • Sucky Documentation (I'm working on it) Sucky文档(我正在研究它)

Checkout this video I created to compare and contrast the differences. 查看我创建的视频 ,比较和对比差异。

I serialize the plugin execution context to file for use with unit tests. 我将插件执行上下文序列化为文件以用于单元测试。 There is a good project on codeplex that does this http://crm2011plugintest.codeplex.com/ 有一个关于codeplex的好项目可以做到这一点http://crm2011plugintest.codeplex.com/

Makes debugging and unit testing easier and you can 'record' real world testing. 使调试和单元测试更容易,您可以“记录”现实世界的测试。

How can I unit test a plugin without having to actually trigger it from CRM, or run through all the hoopla of debugging it in CRM first, and creating unique dummy data for each test? 如何在不必从CRM实际触发插件的情况下对插件进行单元测试,或者首先运行在CRM中调试它的所有箍,并为每个测试创建独特的虚拟数据?

With mocking. 随着嘲弄。 See this link for what classes to mock with RhinoMocks. 有关使用RhinoMocks模拟的类, 请参阅此链接 Sounds like you are on your way in this regard. 在这方面听起来像你正在路上。

How can I use injection to eliminate the need to be deleting, creating, testing, verifying, and deleting data in CRM for each unit test? 如何使用注入来消除在CRM中为每个单元测试删除,创建,测试,验证和删除数据的需要?

Injecting values for the input parameters can be done by stubbing in a hand-cranked instance of the entity you are going to manipulate: 注入输入参数的值可以通过在要操作的实体的手摇实例中进行存根来完成:

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

Isnt that easier than capturing the xml data and rehydrating the entire input parameters collection? 是不是比捕获xml数据和重新整合整个输入参数集更容易?


EDIT: for data access the usual recommendation is to wrap data access into classes. 编辑:对于数据访问,通常的建议是将数据访问包装到类中。 The repository pattern is popular but overkill for what we need here. 存储库模式很受欢迎,但对于我们在这里需要的东西来说太过分了。 For your plugins execution classes, you "inject" your mocked class at creation. 对于你的插件执行类,你可以在创建时“注入”你的模拟类。 A blank constructor that initalizes the default repository, and a second constructor that takes an IRepository. 一个空白构造函数,用于初始化默认存储库,另一个构造函数用于获取IRepository。

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

Depending on the complexity of your plugin steps this can evolve into passing many repositories to each class and could become burdensome but this is the basics you can add complexity to if required. 根据插件步骤的复杂性,这可能会演变为将许多存储库传递给每个类,并且可能会变得很麻烦,但这是您可以根据需要增加复杂性的基础知识。

One really good option would be to use a mocking library which deals with mocks and fakes for you because I wanted to create my own and always ended up wasting a lot of time creating fakes or mocks until I created this library which does it for you. 一个非常好的选择是使用一个模拟库来处理模拟和伪造,因为我想创建我自己的并且总是浪费很多时间创建假货或模拟,直到我创建这个库为你做它。 Try FakeXrmEasy 试试FakeXrmEasy

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM