当我为我的插件运行单元测试时,会抛出以下异常:
以下链接将为您提供堆栈跟踪: Stacktrace
即使我将插件部署并注册到在线实例中,我也会收到相同的消息! 我的插件代码如下:
using System;
using System.Collections.Generic;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
/// <summary>
/// This plugin takes the data provided in the contract lines and makes Unit Orders.. Inside the unit orders, an Alter Unit Orders table is present.
/// The Alter Unit Orders table describes the daily order for each day in the contract's duration.
/// </summary>
namespace DCWIMS.Plugins
{
[CrmPluginRegistration(MessageNameEnum.Update,
"contract",
StageEnum.PreOperation,
ExecutionModeEnum.Synchronous,
"title",
"Post-Update Contract",
1000,
IsolationModeEnum.Sandbox,
Image1Name = "PreImage",
Image1Type = ImageTypeEnum.PreImage,
Image1Attributes = "title")]
public class UnitPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// Extract the tracing service for use in debugging sandboxed plug-ins.
// Wil be registering this plugin, thus will need to add tracing service related code.
ITracingService tracing = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
//obtain execution context from service provider.
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
// The InputParameters colletion contains all the data passed in the message request.
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
//obtain the target entity from the input parameters.
Entity entity = (Entity)context.InputParameters["Target"];
//Obtain Target Entity Id
var targetId = entity.Id.ToString();
//verify that the target entity represents the the contract entity and is active
if (entity.LogicalName != "contract" && entity.GetAttributeValue<OptionSetValue>("statecode").Value != 0)
return;
//obtain the organization service for web service calls.
IOrganizationServiceFactory serviceFactory =
(IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
try
{
//Get Contract StartDate
DateTime startDate = (DateTime)entity["activeon"];
//Get Contract EndDate
DateTime endDate = (DateTime)entity["expireson"];
//Get all weekdays in the contract duration
Eachday range = new Eachday();
var weekdays = range.WeekDay(startDate, endDate); //weekdays list
//Get Contract Number
string contractNumber = (string)entity["contractnumber"];
//Query and aggregate each Weekday's order for the 3 different meal times...
//AM SNACK
string unitsum_am = @" <fetch aggregate='true' distinct='false' >
<entity name='contract' >
<link-entity name='contractdetail' from = 'contractid' to = 'contractid' >
<attribute name='new_mondayunits' alias='new_mondayunits_amsum' aggregate='sum' />
<attribute name='new_tuesdayunits' alias='new_tuesdayunits_amsum' aggregate='sum' />
<attribute name='new_unitswednesday' alias='new_unitswednesday_amsum' aggregate='sum' />
<attribute name='new_unitsthursday' alias='new_unitsthursday_amsum' aggregate='sum' />
<attribute name='new_unitsfriday' alias='new_unitsfriday_amsum' aggregate='sum' />
<filter type='and' >
<condition value='100000001' attribute='new_servingtime' operator= 'eq' />
<condition value='0' attribute='statecode' operator= 'eq' />
<condition value='" + targetId + @"' attribute='contractid' operator= 'eq' />
</filter >
</link-entity>
</entity >
</fetch>";
EntityCollection unitsum_am_result =
service.RetrieveMultiple(new FetchExpression(unitsum_am));
var am_list = new List<int>();
foreach(var unit in unitsum_am_result.Entities)
{
var mondaysum = ((int)((AliasedValue)unit["new_mondayunits_amsum"]).Value);
am_list.Add(mondaysum);
var tuesdaysum = ((int)((AliasedValue)unit["new_tuesdayunits_amsum"]).Value);
am_list.Add(tuesdaysum);
var wednesdaysum= ((int)((AliasedValue)unit["new_unitswednesday_amsum"]).Value);
am_list.Add(wednesdaysum);
var thursdaysum= ((int)((AliasedValue)unit["new_unitsthursday_amsum"]).Value);
am_list.Add(thursdaysum);
var fridaysum= ((int)((AliasedValue)unit["new_unitsfriday_amsum"]).Value);
am_list.Add(fridaysum);
}
//LUNCH
string unitsum_lunch = @" <fetch aggregate='true' distinct='false' >
<entity name='contract' >
<link-entity name='contractdetail' from = 'contractid' to = 'contractid' >
<attribute name='new_mondayunits' alias='new_mondayunits_lunchsum' aggregate='sum' />
<attribute name='new_tuesdayunits' alias='new_tuesdayunits_lunchsum' aggregate='sum' />
<attribute name='new_unitswednesday' alias='new_unitswednesday_lunchsum' aggregate='sum' />
<attribute name='new_unitsthursday' alias='new_unitsthursday_lunchsum' aggregate='sum' />
<attribute name='new_unitsfriday' alias='new_unitsfriday_lunchsum' aggregate='sum' />
<filter type='and' >
<condition value='100000002' attribute='new_servingtime' operator= 'eq' />
<condition value='0' attribute='statecode' operator= 'eq' />
<condition value='" + targetId + @"' attribute='contractid' operator= 'eq' />
</filter >
</link-entity>
</entity >
</fetch>";
EntityCollection unitsum_lunch_result =
service.RetrieveMultiple(new FetchExpression(unitsum_lunch));
var lunch_list = new List<int>();
foreach (var unit in unitsum_lunch_result.Entities)
{
var mondaysum = ((int)((AliasedValue)unit["new_mondayunits_lunchsum"]).Value);
lunch_list.Add(mondaysum);
var tuesdaysum = ((int)((AliasedValue)unit["new_tuesdayunits_lunchsum"]).Value);
lunch_list.Add(tuesdaysum);
var wednesdaysum = ((int)((AliasedValue)unit["new_unitswednesday_lunchsum"]).Value);
lunch_list.Add(wednesdaysum);
var thursdaysum = ((int)((AliasedValue)unit["new_unitsthursday_lunchsum"]).Value);
lunch_list.Add(thursdaysum);
var fridaysum = ((int)((AliasedValue)unit["new_unitsfriday_lunchsum"]).Value);
lunch_list.Add(fridaysum);
}
//PM SNACK
string unitsum_pm = @" <fetch aggregate='true' distinct='false' >
<entity name='contract' >
<link-entity name='contractdetail' from = 'contractid' to = 'contractid' >
<attribute name='new_mondayunits' alias='new_mondayunits_pmsum' aggregate='sum' />
<attribute name='new_tuesdayunits' alias='new_tuesdayunits_pmsum' aggregate='sum' />
<attribute name='new_unitswednesday' alias='new_unitswednesday_pmsum' aggregate='sum' />
<attribute name='new_unitsthursday' alias='new_unitsthursday_pmsum' aggregate='sum' />
<attribute name='new_unitsfriday' alias='new_unitsfriday_pmsum' aggregate='sum' />
<filter type='and' >
<condition value='100000003' attribute='new_servingtime' operator= 'eq' />
<condition value='0' attribute='statecode' operator= 'eq' />
<condition value='" + targetId + @"' attribute='contractid' operator= 'eq' />
</filter >
</link-entity>
</entity >
</fetch>";
EntityCollection unitsum_pm_result =
service.RetrieveMultiple(new FetchExpression(unitsum_pm));
var pm_list = new List<int>();
foreach (var unit in unitsum_pm_result.Entities)
{
var mondaysum = ((int)((AliasedValue)unit["new_mondayunits_pmsum"]).Value);
pm_list.Add(mondaysum);
var tuesdaysum = ((int)((AliasedValue)unit["new_tuesdayunits_pmsum"]).Value);
pm_list.Add(tuesdaysum);
var wednesdaysum = ((int)((AliasedValue)unit["new_unitswednesday_pmsum"]).Value);
pm_list.Add(wednesdaysum);
var thursdaysum = ((int)((AliasedValue)unit["new_unitsthursday_pmsum"]).Value);
pm_list.Add(thursdaysum);
var fridaysum = ((int)((AliasedValue)unit["new_unitsfriday_pmsum"]).Value);
pm_list.Add(fridaysum);
}
foreach(var day in weekdays)
{
var alterunit = new Entity("new_alterunitorder");
alterunit.Attributes.Add("new_orderdate", DateTime.Parse(day));
switch (day.Split(',')[0])
{
case "Monday":
alterunit.Attributes.Add("new_amsnack", am_list[0]);
alterunit.Attributes.Add("new_lunch", lunch_list[0]);
alterunit.Attributes.Add("new_pmsnack", pm_list[0]);
break;
case "Tuesday":
alterunit.Attributes.Add("new_amsnack", am_list[1]);
alterunit.Attributes.Add("new_lunch", lunch_list[1]);
alterunit.Attributes.Add("new_pmsnack", pm_list[1]);
break;
case "Wednesday":
alterunit.Attributes.Add("new_amsnack", am_list[2]);
alterunit.Attributes.Add("new_lunch", lunch_list[2]);
alterunit.Attributes.Add("new_pmsnack", pm_list[2]);
break;
case "Thursday":
alterunit.Attributes.Add("new_amsnack", am_list[3]);
alterunit.Attributes.Add("new_lunch", lunch_list[3]);
alterunit.Attributes.Add("new_pmsnack", pm_list[3]);
break;
case "Friday":
alterunit.Attributes.Add("new_amsnack", am_list[4]);
alterunit.Attributes.Add("new_lunch", lunch_list[4]);
alterunit.Attributes.Add("new_pmsnack", pm_list[4]);
break;
default:
Console.WriteLine($"An unexpected value ({day.Split(',')})");
break;
}
alterunit.Attributes.Add("new_name", contractNumber); //set the record name
//set the unit order record relation
alterunit.Attributes["new_orderlineid"] =
new EntityReference("new_alterunitorder", unitOrderId);
service.Create(alterunit);
}
}
catch (FaultException<OrganizationServiceFault> ex)
{
throw new InvalidPluginExecutionException("An error occured.. Phil is responsible.", ex);
}
catch (Exception ex)
{
tracing.Trace("An Error Occured: {0}", ex.ToString());
throw;
}
}
}
}
}
以下是每天的代码:
using System;
using System.Collections.Generic;
namespace DCWIMS.Plugins
{
public class Eachday
{
public List<string> WeekDay(DateTime from, DateTime thru)
{
List<string> days_list = new List<string>();
for (var day = from.Date; day.Date <= thru.Date; day = day.AddDays(1))
{
days_list.Add(day.ToLongDateString());
if (day.DayOfWeek == DayOfWeek.Sunday || day.DayOfWeek == DayOfWeek.Saturday)
days_list.Remove(day.ToLongDateString());
}
return days_list;
}
}
}
我的单元测试如下:
using DCWIMS.Plugins;
using Microsoft.Crm.Sdk.Fakes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Xrm.Sdk;
namespace Plugins.Tests
{
[TestClass]
public class UnitTest1
{
[TestMethod]
[TestCategory("Unit Test")]
public void TestUnitPlugin()
{
using (var pipline = new PluginPipeline(FakeMessageNames.Update, FakeStages.PreOperation, new Entity("contract")))
{
var plugin = new UnitPlugin();
pipline.Execute(plugin);
}
}
}
}
即使我在实际的CRM实例上注册了插件,我也收到以下错误消息:
这是从CRM Online检索到的日志文件!
Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: Unexpected exception from plug-in (Execute): DCWIMS.Plugins.UnitPlugin: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.Detail:
<OrganizationServiceFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<ActivityId>4025e0f8-eed5-4b7f-a3b1-52a9f2a6f2cc</ActivityId>
<ErrorCode>-2147220956</ErrorCode>
<ErrorDetails xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
<Message>Unexpected exception from plug-in (Execute): DCWIMS.Plugins.UnitPlugin: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.</Message>
<Timestamp>2018-07-20T17:47:36.4061434Z</Timestamp>
<ExceptionRetriable>false</ExceptionRetriable>
<ExceptionSource i:nil="true" />
<InnerFault i:nil="true" />
<OriginalException i:nil="true" />
<TraceText>
[Plugins: DCWIMS.Plugins.UnitPlugin]
[dbb33fa1-448c-e811-815c-480fcff4b5b1: Pre-Create Contract]
An Error Occured: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at Microsoft.Xrm.Sdk.AttributeCollection.get_Item(String attributeName)
at DCWIMS.Plugins.UnitPlugin.Execute(IServiceProvider serviceProvider)
</TraceText>
</OrganizationServiceFault
>
答案 0 :(得分:1)
单元测试不是我的强项。但我会尽力指导您。
首先,我对单元测试的看法。我对Dynamics不太重视,因为它是一个难以控制的环境。您可以测试您的业务逻辑是否在插件中得到了很好的实现,但是JavaScript,业务规则,工作流或操作可能会使您的逻辑脱轨。您的插件可以显示为蓝色,但是异步工作流之后会立即将其更改为红色。
相反,我更喜欢UI测试。它将测试从A到Z的过程,并强制执行上述所有组件。我建议您使用一个不错的框架:https://github.com/Microsoft/EasyRepro
话虽如此。要解决您的问题,您还需要模拟组织服务并返回假数据。我的意思是这一行:
EntityCollection unitsum_am_result = service.RetrieveMultiple(new FetchExpression(unitsum_am));
可悲的是,即使您进行了模拟,也无法真正解析fetchXml并对其进行验证。
然后,在返回伪数据之后,您应该断言这一行,创建一个具有正确总和的实体:
service.Create(alterunit);