Skip to content

tims205/DataversePluginTestingForLazyPeople

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dataverse Plugin Testing and Debugging for Lazy People

This example illustrates how we can use a Unit Test Project and Moq to simulate execution of a Dataverse plugin and develop/debug directly in Visual Studio against a real Organization Service without having to continuously deploy new versions of the plugin.

The aim here is to help speed up plugin development by giving the developer a way of quickly and easily examining how their plugin is behaving. Instead of mocking all the data that your plugin needs, this uses the actual organization service for an environment and will create/update/delete/read real Dataverse data, so is only appropriate for use against development environments.

The steps below go through the setup of a basic plugin project and unit test project that invokes the plugin.

Setup Instructions

1. Initialise a new Dataverse Plugin Project and Visual Studio Solution

  1. Create a directory to hold the Dataverse plugin project and testing project. Run the following command in a terminal:
cd C:\{YourReposPath}
mkdir PluginTestingForLazyPeople
cd C:\{YourReposPath}\PluginTestingForLazyPeople
  1. Create a new Dataverse plugin project using the PAC CLI. Run the following command in a terminal:
pac plugin init --author You --outputDirectory C:\{YourReposPath}\PluginTestingForLazyPeople\ExamplePluginTests
  1. Open Visual Studio and select 'Open a project or solution'.

  2. Select the ExamplePluginTests.csproj file that was created.

  3. When Visual Studio opens, select the 'ExamplePluginTests' solution, then click File --> Save ExamplePluginTests.sln As.. and save the .sln file in the PluginTestingForLazyPeople directory.

2. Add a Unit Test Project

  1. Right click on the Solution and select Add --> New Project

  2. Select 'Unit Test Project (.NET Framework)'

  3. Right click on the Unit Test Project that has been created and select Build Dependencies --> Project Dependencies

  4. Add the DataversePlugin project as a dependency

3. Add Moq to the Unit Test Project

  1. Right click on 'References' in the Unit Test Project and select Migrate packages.config to PackageReference...

  2. Right click on References again and select Manage NuGet Packages...

  3. Search for and install 'Moq'

4. Add Dataverse Service Client package to the Unit Test Project

We are going to create a Dataverse ServiceClient in the Unit Test Project and use it to pass a mock organization service into the plugin. This will then allow us to set breakpoints and debug plugin execution directly in Visual Studio without having to continuously deploy updated plugins to the server.

  1. Right click on 'References' in the Unit Test Project and select Manage NuGet Packages...

  2. Search for and install Microsoft.PowerPlatform.Dataverse.Client

5. Create a ServiceClient inside a TestMethod

  1. Within TestMethod1() we want to create a ServiceClient instance when the test runs. Update the file UnitTest1.cs as follows:

    using Microsoft.PowerPlatform.Dataverse.Client;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System;
    
    namespace LazyPluginTests
    {
        [TestClass]
        public class UnitTest1
        {
            [TestMethod]
            public void TestMethod1()
            {
                string connString = "AuthType=OAuth;Url=https://{{YOURORG}}.crm.dynamics.com/;Username={{YOURUSERNAME}};RedirectUri=http://localhost;AppId=51f81489-12ee-4a9e-aaae-a2591f45987d;LoginPrompt=Auto";
    
                ServiceClient serviceClient = new ServiceClient(connString);
    
            }
        }
    }

    In our example, a login prompt will appear each time the test is run. See this article for using different connection strings: https://learn.microsoft.com/en-us/power-apps/developer/data-platform/xrm-tooling/use-connection-strings-xrm-tooling-connect

  2. Right click on the Unit Test Project and select Build

  3. We are now going to attempt to run a test and observe that further setup is required. Expand the Test Explorer in Visual Studio, locate TestMethod1 and click Run

  4. Observe that the test fails to run and returns an error about mismatched assembly manifests. This is a result of adding the Microsoft.PowerPlatform.Dataverse.Client assembly. To fix this, we will need to add binding redirects to the Unit Test Project

6. Add Assembly Binding Redirects

  1. Right click on the Unit Test Project and select Add --> New Item

  2. Set the file name to app.config and click Add

  3. Update the app.config file as follows:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <runtime>
            <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
                <dependentAssembly>
                    <assemblyIdentity name="Microsoft.Extensions.DependencyInjection.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral"/>
                    <bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.8.0"/>
                </dependentAssembly>
                <dependentAssembly>
                    <assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
                    <bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0"/>
                </dependentAssembly>
                <dependentAssembly>
                    <assemblyIdentity name="Microsoft.Extensions.Logging" publicKeyToken="adb9793829ddae60" culture="neutral"/>
                    <bindingRedirect oldVersion="0.0.0.0-3.1.8.0" newVersion="3.1.8.0"/>
                </dependentAssembly>
                <dependentAssembly>
                    <assemblyIdentity name="Microsoft.Extensions.Options" publicKeyToken="adb9793829ddae60" culture="neutral"/>
                    <bindingRedirect oldVersion="0.0.0.0-3.1.8.0" newVersion="3.1.8.0"/>
                </dependentAssembly>
                <dependentAssembly>
                    <assemblyIdentity name="Microsoft.Extensions.Logging.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral"/>
                    <bindingRedirect oldVersion="0.0.0.0-3.1.8.0" newVersion="3.1.8.0"/>
                </dependentAssembly>
                <dependentAssembly>
                    <assemblyIdentity name="Microsoft.Extensions.Caching.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral"/>
                    <bindingRedirect oldVersion="0.0.0.0-3.1.8.0" newVersion="3.1.8.0"/>
                </dependentAssembly>
                <dependentAssembly>
                    <assemblyIdentity name="Microsoft.Extensions.Primitives" publicKeyToken="adb9793829ddae60" culture="neutral"/>
                    <bindingRedirect oldVersion="0.0.0.0-3.1.8.0" newVersion="3.1.8.0"/>
                </dependentAssembly>
            </assemblyBinding>
        </runtime>
    </configuration>

  4. Right click on the Unit Test Project and select Rebuild

  5. Attempt to run the TestMethod1 test again. This time we see that we are prompted for a login and the test should pass (note that nothing is actually being tested or asserted)

7. Mock the IServiceProvider

When a plugin executes on the platform an IServiceProvider instance is provided to the Execute() method of the plugin.

This gives us access to the services we use in a plugin (like the Organization Service) and is used to access the plugin execution context (such as the entity and event that triggered the plugin).

In TestMethod1() we are going to create a Mock IServiceProvider using Moq, which will give us a way to simulate plugin execution by running a test and examine how a plugin behaves using a 'live' organization service.

Add the following code to TestMethod1():

var mockServiceProvider = new Moq.Mock<IServiceProvider>();
mockServiceProvider.Setup(x => x.GetService(typeof(IOrganizationService))).Returns(serviceClient);

/// Mocking the plugin execution context. In this example we are not providing any specific context such as message or target entity, but you can set it up as needed for your tests.
var mockExecutionContext = new Moq.Mock<IExecutionContext>();
mockExecutionContext.Setup(x => x.OperationCreatedOn).Returns(DateTime.Now);
mockExecutionContext.Setup(x => x.UserId).Returns(Guid.NewGuid());
mockExecutionContext.Setup(x => x.InitiatingUserId).Returns(Guid.NewGuid());
   
var mockPluginContext = new Moq.Mock<IPluginExecutionContext>();
mockPluginContext.Setup(x => x.UserId).Returns(Guid.NewGuid());
mockPluginContext.Setup(x => x.InitiatingUserId).Returns(Guid.NewGuid());

// This provides the 'real' service client we have created to the plugin
var mockServiceFactory = new Moq.Mock<IOrganizationServiceFactory>();
mockServiceFactory.Setup(x => x.CreateOrganizationService(It.IsAny<Guid>())).Returns(serviceClient);

var mockTracingService = new Moq.Mock<ITracingService>();

mockServiceProvider.Setup(x => x.GetService(typeof(ITracingService))).Returns(mockTracingService.Object);
mockServiceProvider.Setup(x => x.GetService(typeof(IOrganizationServiceFactory))).Returns(mockServiceFactory.Object);
mockServiceProvider.Setup(x => x.GetService(typeof(IPluginExecutionContext))).Returns(mockPluginContext.Object);
mockServiceProvider.Setup(x => x.GetService(typeof(IExecutionContext))).Returns(mockExecutionContext.Object);

8. Add a reference to the Dataverse plugin

  1. Right click on References in the Unit Test Project and select Add Reference

  2. Select Projects and then your DataversePlugin project

9. Invoke the Dataverse Plugin from TestMethod1()

  1. Update UnitTest1.cs to add a reference to the plugin project. Add the following using statement to the file:
    using DataversePlugin;
  2. Add the following code to TestMethod1() to invoke the Dataverse Plugin when the test is run:
    // Create an instance of the plugin and execute it with the mocked service provider.
    // The Plugin1 constructor requires unsecure and secure configuration strings, which can be null for testing purposes.
    var examplePlugin = new Plugin1(null, null);
    examplePlugin.Execute(mockServiceProvider.Object);

10. Debug test and observe plugin behaviour

In this example I have updated Plugin1.cs to perform some basic operations in order to demonstrate how we can observe and debug the plugin inside Visual Studio.

   protected override void ExecuteDataversePlugin(ILocalPluginContext localPluginContext)
   {
       if (localPluginContext == null)
       {
           throw new ArgumentNullException(nameof(localPluginContext));
       }

       var context = localPluginContext.PluginExecutionContext;

       // Create a record
       Entity newContact = new Entity("contact");
       newContact["firstname"] = $"Test Contact {DateTime.Now.Ticks}";

       Guid newContactId = localPluginContext.PluginUserService.Create(newContact);

       // Retrieve some records
       QueryExpression getContacts = new QueryExpression("contact");
       getContacts.TopCount = 5;
       getContacts.ColumnSet = new ColumnSet("firstname");

       EntityCollection contactsEC = localPluginContext.PluginUserService.RetrieveMultiple(getContacts);

       var contactOne = contactsEC.Entities.FirstOrDefault();
   }

If we add breakpoints to our plugin code:

And then 'Debug' TestMethod1():

The breakpoints in our plugin will be hit, and we can inspect what the plugin is doing and how it behaves against a real organization service.

Here we can see the result of a new record being created:

And the results from a query:

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages