Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Add the option to register custom mock services #211

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 50 additions & 4 deletions src/XrmMockupShared/MockupServiceProviderAndFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
using System.Text;
using System.Threading.Tasks;

namespace DG.Tools.XrmMockup {
namespace DG.Tools.XrmMockup
{

/// <summary>
/// A factory used to generate MockupServices based on a given Mockup instance and pluginContext
Expand All @@ -16,29 +17,74 @@ internal class MockupServiceProviderAndFactory : IServiceProvider, IOrganization
private Core core;
private ITracingService tracingService;
private PluginContext pluginContext;
private Dictionary<Type, object> mockServices;

/// <summary>
/// Creates new MockupServiceProviderAndFactory object
/// </summary>
/// <param name="core"></param>
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<Type, object>();
}

/// <summary>
/// Get service from servicetype. Returns null if unknown type
/// Get service from servicetype. Returns null if unknown type, new types can be added with <see cref="AddCustomService"/> if needed.
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
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;
}

/// <summary>
/// Add a custom mock service.
/// This will not override the default XrmMockup services.
/// </summary>
/// <typeparam name="TypeT"></typeparam>
/// <param name="service"></param>
public void AddCustomService<T>(T service)
{
mockServices.Add(typeof(T), service);
}

/// <summary>
/// Remove a custom mock service.
/// This will not affect the default XrmMockup services.
/// </summary>
/// <typeparam name="TypeT"></typeparam>
/// <param name="service"></param>
public void RemoveCustomService<T>()
{
mockServices.Remove(typeof(T));
}

/// <summary>
/// Clear all custom mock services.
/// This will not affect the default XrmMockup services.
/// </summary>
public void ClearCustomServices()
{
mockServices.Clear();
}

/// <summary>
Expand Down
31 changes: 31 additions & 0 deletions src/XrmMockupShared/XrmMockupBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,37 @@ public void PopulateWith(params Entity[] entities) {
Core.PopulateWith(entities);
}

/// <summary>
/// Add a custom mock service.
/// This will not override the default XrmMockup services.
/// </summary>
/// <typeparam name="T">e.g. IServiceEndpointNotificationService</typeparam>
/// <param name="service"></param>
public void AddService<T>(T service)
{
ServiceFactory.AddCustomService(service);
}

/// <summary>
/// Remove a custom mock service.
/// This will not override the default XrmMockup services.
/// </summary>
/// <typeparam name="T">e.g. IServiceEndpointNotificationService</typeparam>
/// <param name="service"></param>
public void RemoveService<T>()
{
ServiceFactory.RemoveCustomService<T>();
}

/// <summary>
/// Clear all custom mock services.
/// This will not affect the default XrmMockup services.
/// </summary>
public void ResetServices()
{
ServiceFactory.ClearCustomServices();
}

/// <summary>
/// Gets a system administrator organization service
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// This plugin is used to test custom mock service registrations, the actual endpoint id doesn't matter here
/// </summary>
public class SendContactToServiceEndpointPlugin : Plugin
{
public SendContactToServiceEndpointPlugin() : base(typeof(SendContactToServiceEndpointPlugin))
{
RegisterPluginStep<Contact>(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<Contact>();

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);
}
}
}
}
2 changes: 2 additions & 0 deletions tests/SharedPluginsAndCodeactivites/Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Closeincidentplugin.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ContactSendToServiceEndpointPlugin.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ParentPostCreatePlugin.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ChildPreCreatePlugin.cs" />
<Compile Include="$(MSBuildThisFileDirectory)NotePostOperationPlugin.cs" />
Expand Down
1 change: 1 addition & 0 deletions tests/SharedTests/SharedTests.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<Compile Include="$(MSBuildThisFileDirectory)TestCascade.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TestCaseOrderer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TestClone.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TestCustomService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TestCWAAccountOptional.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TestDefaultBusinessUnitTeamsMembers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TestPriorityAttribute.cs" />
Expand Down
94 changes: 94 additions & 0 deletions tests/SharedTests/TestCustomService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using Xunit;
using Microsoft.Xrm.Sdk;
using DG.XrmFramework.BusinessDomain.ServiceContext;
using System.Collections.Generic;

namespace DG.XrmMockupTest
{
public class TestCustomService : UnitTestBase
{
class MockServiceEndpointNotificationService : IServiceEndpointNotificationService
{
public string Execute(EntityReference serviceEndpoint, IExecutionContext context)
{
return null;
}
}

private const string contactDescription = "Test_IServiceEndpointNotificationService";

public TestCustomService(XrmMockupFixture fixture) : base(fixture)
{
crm.ResetServices();
}

[Fact]
public void CustomServiceShouldBeAvailableInPlugin()
{
var customService = new MockServiceEndpointNotificationService();
crm.AddService<IServiceEndpointNotificationService>(customService);

orgAdminUIService.Create(new Contact() { Description = contactDescription });
}

[Fact]
public void ShouldThrowExceptionOnDuplicateRegistration()
{
var customService = new MockServiceEndpointNotificationService();
crm.AddService<IServiceEndpointNotificationService>(customService);

Assert.Throws<System.ArgumentException>(() =>
{
var customService2 = new MockServiceEndpointNotificationService();
crm.AddService<IServiceEndpointNotificationService>(customService2);
});
}

[Fact]
public void ShouldThrowKeyNotFoundExceptionIfServiceIsNotFound()
{
Assert.Throws<KeyNotFoundException>(() =>
{
orgAdminUIService.Create(new Contact() { Description = contactDescription });
});

crm.ResetServices();
}

[Fact]
public void ShouldThrowKeyNotFoundExceptionIfServiceIsRemoved()
{
var customService = new MockServiceEndpointNotificationService();
crm.AddService<IServiceEndpointNotificationService>(customService);
crm.RemoveService<IServiceEndpointNotificationService>();

Assert.Throws<KeyNotFoundException>(() =>
{
orgAdminUIService.Create(new Contact() { Description = contactDescription });
});
}

[Fact]
public void ShouldThrowKeyNotFoundExceptionIfServicesAreReset()
{
var customService = new MockServiceEndpointNotificationService();
crm.AddService<IServiceEndpointNotificationService>(customService);
crm.ResetServices();

Assert.Throws<KeyNotFoundException>(() =>
{
orgAdminUIService.Create(new Contact() { Description = contactDescription });
});
}

[Fact]
public void CustomServiceShouldStayAvailableAfterEnvironmentReset()
{
var customService = new MockServiceEndpointNotificationService();
crm.AddService<IServiceEndpointNotificationService>(customService);
crm.ResetEnvironment();

orgAdminUIService.Create(new Contact() { Description = "Test_IServiceEndpointNotificationService" });
}
}
}