Unit test

You should create unittest for your plugins, but from a unit test point of view, there should't be a lot a scenarios where you need to know that the code will eventually run within a Dynamics 365 CE plugin execution pipeline.

You should inject Entities (or jummy jummy interfaces that expose parts of these), services, IQueryable, IRepository into your services, and as long as you stick to these, unit test is unit test, and the fact that it eventually runs within Dynamics 365 should not make any difference.

If you often find you self need some of the "standard dynamics 365 services", such as IOrganizationService, because you access these directly, you are not really taking full advantage of this framework.

Fake IOrganizationService

Despite above, basically stating you do not need to mock the standard services of Dynamics 365 CE, there are situations where you wish to do some unit test, including standard services - anyway. This framework does have a XRM fake impl.
Create a .NET Framework unit test library, and add the NuGet dependecy: Kipon.Solid.Plugin.Fake to you unit library, and also add the project dependency on your plugin library to the unit test library, and you are ready to go.

You could ask your self, why not just use Fake.Xrm, it has been around for a while, and is pretty cool, and you are to some degree right, and please go-ahead if you like. The only problem with that approach is, that Fake.Xrm does not know the specific flawor a Kipon.Solid.Plugin, so your unit-test will endup doing a lot of trivial wireing. That is why a Kipon.Xrm.Fake is on its way, to bridge that gab.

Unit test example

A Kipon.Solid.Plugin.Fake unit test is basically just a normal unittest, however it has the ability to emulate a Dynamics 365 CE pipeline context, including all related service with a very limited effort:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
namespace Kipon.PluginExample.UT.Plugins.Account
    public class api0_AccountPluginTest
        public void Test()
            var accountId = Guid.NewGuid();
            using (var ctx = Kipon.Xrm.Fake.Repository.PluginExecutionFakeContext.ForType<Kipon.PluginExample.Plugins.Account.api0.AccountPlugin>())
                ctx.AddEntity(new Kipon.PluginExample.Entities.Account { AccountId = accountId, Name = "Test pre account" });
                ctx.OnPre = delegate
                    // performe som test that is relevant after execution of Pre
                ctx.Update(new Kipon.PluginExample.Entities.Account { AccountId = accountId, Name = "Test new a account name" });

Take a look at above example. The using statement is create a plugin pipeline context for a specific plugin, based on the plugin type.

Because our plugin is listening for update event onpre for account, we add the image of the account as we expect it too look before the operation.

Then we setup an onpre delegate. This method will be called by the fake context AFTER the pre-event. This allow you to do validation to see if the event actually did with the entity what you expected. An empty delegate as the examples show is actually relevant. Because if you missspelled the OnPreUpdate ... method name in your plugin, the unit test framework will actually throw an excepton, because you create an assert onpre that will never be hit. That is considered a bug. This simple way you can create unit test that verifies that your methods will actually be called.

Finally we call the update event on the ctx. That part of the code i emulating what the ui, or any other client would do against an organization service.

Be carefull to "overuse" the ability to create unit test from plugins. These unit test should mainly test that events are called. You should test your services individually and make sure each component in your code has it own unit test.

When it comes to components, try live by the rule "small and simple is beautifull". The more you put into a single scope of unit test, the harder it is to write, and it can become impossible to get a coverage around 100% due to complexity.

© Kipon ApS 2020, 2021, 2022, 2023. All content on the page is the property of Kipon ApS. Any republish or copy of this content is a violation. The content of this site is NOT open source, and cannot be copied, republished or used in any context without explcit permission from the owner.