From 1c38de9c1201861bd50c76ed83f1400c88769a10 Mon Sep 17 00:00:00 2001 From: Lisa Gonaus Date: Wed, 5 Oct 2022 14:47:42 +0200 Subject: [PATCH 1/4] Set service provider in the base plugin class --- tests/SharedPluginsAndCodeactivites/Plugin.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/SharedPluginsAndCodeactivites/Plugin.cs b/tests/SharedPluginsAndCodeactivites/Plugin.cs index d0ea3fd7..99b6eb01 100644 --- a/tests/SharedPluginsAndCodeactivites/Plugin.cs +++ b/tests/SharedPluginsAndCodeactivites/Plugin.cs @@ -71,6 +71,8 @@ internal LocalPluginContext(IServiceProvider serviceProvider) throw new ArgumentNullException("serviceProvider"); } + this.ServiceProvider = serviceProvider; + // Obtain the execution context service from the service provider. this.PluginExecutionContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); From 6c1be6c486efe4b80a4d7c7d8803b678a663f212 Mon Sep 17 00:00:00 2001 From: Lisa Gonaus Date: Wed, 5 Oct 2022 16:03:18 +0200 Subject: [PATCH 2/4] Add unit tests that describe the desired behaviour --- .../ContactSendToServiceEndpointPlugin.cs | 35 +++++++ .../SharedPluginsAndCodeactivites.projitems | 1 + tests/SharedTests/SharedTests.projitems | 1 + tests/SharedTests/TestCustomService.cs | 94 +++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 tests/SharedPluginsAndCodeactivites/ContactSendToServiceEndpointPlugin.cs create mode 100644 tests/SharedTests/TestCustomService.cs diff --git a/tests/SharedPluginsAndCodeactivites/ContactSendToServiceEndpointPlugin.cs b/tests/SharedPluginsAndCodeactivites/ContactSendToServiceEndpointPlugin.cs new file mode 100644 index 00000000..7e801641 --- /dev/null +++ b/tests/SharedPluginsAndCodeactivites/ContactSendToServiceEndpointPlugin.cs @@ -0,0 +1,35 @@ +using DG.Some.Namespace; +using DG.XrmFramework.BusinessDomain.ServiceContext; +using Microsoft.Xrm.Sdk; +using System; +using System.Collections.Generic; +using System.Text; + +namespace SharedPluginsAndCodeactivites +{ + /// + /// This plugin is used to test custom mock service registrations, the actual endpoint id doesn't matter here + /// + public class SendContactToServiceEndpointPlugin : Plugin + { + public SendContactToServiceEndpointPlugin() : base(typeof(SendContactToServiceEndpointPlugin)) + { + RegisterPluginStep(EventOperation.Create, ExecutionStage.PreOperation, Execute) + .SetExecutionMode(ExecutionMode.Synchronous); + } + + protected void Execute(LocalPluginContext localContext) + { + var target = localContext.PluginExecutionContext.InputParameters["Target"] as Entity; + var contact = target.ToEntity(); + + if (contact.Description == "Test_IServiceEndpointNotificationService") + { + var serviceEndpoint = (IServiceEndpointNotificationService)localContext.ServiceProvider.GetService(typeof(IServiceEndpointNotificationService)); + var endpointReference = new EntityReference("serviceendpoint", Guid.Empty); + + serviceEndpoint.Execute(endpointReference, localContext.PluginExecutionContext); + } + } + } +} diff --git a/tests/SharedPluginsAndCodeactivites/SharedPluginsAndCodeactivites.projitems b/tests/SharedPluginsAndCodeactivites/SharedPluginsAndCodeactivites.projitems index 11c7fc45..a2860d0f 100644 --- a/tests/SharedPluginsAndCodeactivites/SharedPluginsAndCodeactivites.projitems +++ b/tests/SharedPluginsAndCodeactivites/SharedPluginsAndCodeactivites.projitems @@ -10,6 +10,7 @@ + diff --git a/tests/SharedTests/SharedTests.projitems b/tests/SharedTests/SharedTests.projitems index b1c08505..5183a341 100644 --- a/tests/SharedTests/SharedTests.projitems +++ b/tests/SharedTests/SharedTests.projitems @@ -17,6 +17,7 @@ + diff --git a/tests/SharedTests/TestCustomService.cs b/tests/SharedTests/TestCustomService.cs new file mode 100644 index 00000000..1573b87d --- /dev/null +++ b/tests/SharedTests/TestCustomService.cs @@ -0,0 +1,94 @@ +using Xunit; +using Microsoft.Xrm.Sdk; +using DG.XrmFramework.BusinessDomain.ServiceContext; +using System.Collections.Generic; + +namespace DG.XrmMockupTest +{ + class MockServiceEndpointNotificationService : IServiceEndpointNotificationService + { + public string Execute(EntityReference serviceEndpoint, IExecutionContext context) + { + return null; + } + } + + public class TestCustomService : UnitTestBase + { + private const string contactDescription = "Test_IServiceEndpointNotificationService"; + + public TestCustomService(XrmMockupFixture fixture) : base(fixture) + { + crm.ResetServices(); + } + + [Fact] + public void CustomServiceShouldBeAvailableInPlugin() + { + var customService = new MockServiceEndpointNotificationService(); + crm.AddService(customService); + + orgAdminUIService.Create(new Contact() { Description = contactDescription }); + } + + [Fact] + public void ShouldThrowExceptionOnDuplicateRegistration() + { + var customService = new MockServiceEndpointNotificationService(); + crm.AddService(customService); + + Assert.Throws(() => + { + var customService2 = new MockServiceEndpointNotificationService(); + crm.AddService(customService2); + }); + } + + [Fact] + public void ShouldThrowKeyNotFoundExceptionIfServiceIsNotFound() + { + Assert.Throws(() => + { + orgAdminUIService.Create(new Contact() { Description = contactDescription }); + }); + + crm.ResetServices(); + } + + [Fact] + public void ShouldThrowKeyNotFoundExceptionIfServiceIsRemoved() + { + var customService = new MockServiceEndpointNotificationService(); + crm.AddService(customService); + crm.RemoveService(); + + Assert.Throws(() => + { + orgAdminUIService.Create(new Contact() { Description = contactDescription }); + }); + } + + [Fact] + public void ShouldThrowKeyNotFoundExceptionIfServicesAreReset() + { + var customService = new MockServiceEndpointNotificationService(); + crm.AddService(customService); + crm.ResetServices(); + + Assert.Throws(() => + { + orgAdminUIService.Create(new Contact() { Description = contactDescription }); + }); + } + + [Fact] + public void CustomServiceShouldStayAvailableAfterEnvironmentReset() + { + var customService = new MockServiceEndpointNotificationService(); + crm.AddService(customService); + crm.ResetEnvironment(); + + orgAdminUIService.Create(new Contact() { Description = "Test_IServiceEndpointNotificationService" }); + } + } +} From c71f6394803e1a1326298cd1300910a8075a38e7 Mon Sep 17 00:00:00 2001 From: Lisa Gonaus Date: Wed, 5 Oct 2022 16:07:30 +0200 Subject: [PATCH 3/4] Add the option to register custom services to enable support for e.g. IServiceEndpointNotificationService mocks --- .../MockupServiceProviderAndFactory.cs | 54 +++++++++++++++++-- src/XrmMockupShared/XrmMockupBase.cs | 31 +++++++++++ 2 files changed, 81 insertions(+), 4 deletions(-) diff --git a/src/XrmMockupShared/MockupServiceProviderAndFactory.cs b/src/XrmMockupShared/MockupServiceProviderAndFactory.cs index ddd41067..d59b834c 100644 --- a/src/XrmMockupShared/MockupServiceProviderAndFactory.cs +++ b/src/XrmMockupShared/MockupServiceProviderAndFactory.cs @@ -6,7 +6,8 @@ using System.Text; using System.Threading.Tasks; -namespace DG.Tools.XrmMockup { +namespace DG.Tools.XrmMockup +{ /// /// A factory used to generate MockupServices based on a given Mockup instance and pluginContext @@ -16,6 +17,7 @@ internal class MockupServiceProviderAndFactory : IServiceProvider, IOrganization private Core core; private ITracingService tracingService; private PluginContext pluginContext; + private Dictionary mockServices; /// /// Creates new MockupServiceProviderAndFactory object @@ -23,14 +25,16 @@ internal class MockupServiceProviderAndFactory : IServiceProvider, IOrganization /// public MockupServiceProviderAndFactory(Core core) : this(core, null, new TracingService()) { } - internal MockupServiceProviderAndFactory(Core core, PluginContext pluginContext, ITracingService tracingService) { + internal MockupServiceProviderAndFactory(Core core, PluginContext pluginContext, ITracingService tracingService) + { this.core = core; this.pluginContext = pluginContext; this.tracingService = tracingService; + this.mockServices = core.ServiceFactory?.mockServices ?? new Dictionary(); } /// - /// Get service from servicetype. Returns null if unknown type + /// Get service from servicetype. Returns null if unknown type, new types can be added with if needed. /// /// /// @@ -38,7 +42,49 @@ public object GetService(Type serviceType) { if (serviceType == typeof(IPluginExecutionContext)) return this.pluginContext; if (serviceType == typeof(ITracingService)) return this.tracingService; if (serviceType == typeof(IOrganizationServiceFactory)) return this; - return null; + + mockServices.TryGetValue(serviceType, out object customService); + + if(customService == null) + { + var errorMessage = $"No service with the type {serviceType} found.\n" + + $"Only {nameof(IPluginExecutionContext)}, {nameof(ITracingService)} and {nameof(IOrganizationServiceFactory)} are supported by default.\n" + + $"Other mock services need to be registered in the mock crm context by yourself."; + throw new KeyNotFoundException(errorMessage); + } + + return customService; + } + + /// + /// Add a custom mock service. + /// This will not override the default XrmMockup services. + /// + /// + /// + public void AddCustomService(T service) + { + mockServices.Add(typeof(T), service); + } + + /// + /// Remove a custom mock service. + /// This will not affect the default XrmMockup services. + /// + /// + /// + public void RemoveCustomService() + { + mockServices.Remove(typeof(T)); + } + + /// + /// Clear all custom mock services. + /// This will not affect the default XrmMockup services. + /// + public void ClearCustomServices() + { + mockServices.Clear(); } /// diff --git a/src/XrmMockupShared/XrmMockupBase.cs b/src/XrmMockupShared/XrmMockupBase.cs index 07c75990..2b571ac7 100644 --- a/src/XrmMockupShared/XrmMockupBase.cs +++ b/src/XrmMockupShared/XrmMockupBase.cs @@ -220,6 +220,37 @@ public void PopulateWith(params Entity[] entities) { Core.PopulateWith(entities); } + /// + /// Add a custom mock service. + /// This will not override the default XrmMockup services. + /// + /// e.g. IServiceEndpointNotificationService + /// + public void AddService(T service) + { + ServiceFactory.AddCustomService(service); + } + + /// + /// Remove a custom mock service. + /// This will not override the default XrmMockup services. + /// + /// e.g. IServiceEndpointNotificationService + /// + public void RemoveService() + { + ServiceFactory.RemoveCustomService(); + } + + /// + /// Clear all custom mock services. + /// This will not affect the default XrmMockup services. + /// + public void ResetServices() + { + ServiceFactory.ClearCustomServices(); + } + /// /// Gets a system administrator organization service /// From d668cbe82f69fbf7af6f9affa496803d957f9e48 Mon Sep 17 00:00:00 2001 From: Lisa Gonaus Date: Wed, 5 Oct 2022 17:38:59 +0200 Subject: [PATCH 4/4] Change MockServiceEndpointNotificationService to a nested class since it's only needed in the TestCustomService --- tests/SharedTests/TestCustomService.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/SharedTests/TestCustomService.cs b/tests/SharedTests/TestCustomService.cs index 1573b87d..1a71971e 100644 --- a/tests/SharedTests/TestCustomService.cs +++ b/tests/SharedTests/TestCustomService.cs @@ -5,16 +5,16 @@ namespace DG.XrmMockupTest { - class MockServiceEndpointNotificationService : IServiceEndpointNotificationService + public class TestCustomService : UnitTestBase { - public string Execute(EntityReference serviceEndpoint, IExecutionContext context) + class MockServiceEndpointNotificationService : IServiceEndpointNotificationService { - return null; + public string Execute(EntityReference serviceEndpoint, IExecutionContext context) + { + return null; + } } - } - public class TestCustomService : UnitTestBase - { private const string contactDescription = "Test_IServiceEndpointNotificationService"; public TestCustomService(XrmMockupFixture fixture) : base(fixture)