Dataverse plugins test automations with FakeXrmEasy Pipeline Simulator

The value of creating unit tests in the development process no longer needs to be explained to anyone. Implementing and running automated tests as a part of the development process has also been adopted in the world of Power Platform pro-code extensions. Another area of testing automation, which I believe is not as common as the above-mentioned one, is integration testing. What integration tests really are? I’ve encountered different approaches to their definitions. When some developers talk about integration testing, they mean automated tests that run multiple components or code layers of their applications. Or tests that use “real” data repositories (not the ones mocked inside the memory of the computer running tests). Or even tests of integration components of our solution... The last one is a joke, of course 😊.

I would like to expand this definition by another point from the Dataverse plugins’ development world. Integration tests may be about checking the results of multiple plugins, run inside the plugin’s execution pipeline.

Let’s imagine the following situation: We would like to rate our Opportunities’ records as “cold”, “warm” or “hot”. This value should be dependent on the value of the “Annual revenue” attribute on the related Account. We may implement this functionality as a plugin run on the pre-create event of the Opportunity record. We may also want to create notifications sent to the Opportunity Owner’s Manager when there is a new Opportunity with “hot” rating. So, we would like to create another plugin right now on the Opportunities post-create stage. Yeah, I’m aware we may create similar functionality with low-code tools (like Power Automate) right now. For our example, let’s keep it as a pro-code-based solution, however.

Let’s imagine we would like to test the following end-to-end scenario:

When a new Opportunity is created for the Account with annual revenue above 10 million euros, an email notification should be sent to the Account’s Owner Manager.

The above-described functionality has been implemented with two plugins run on the opportunity creation pre- and post-stages. We may write unit tests for both plugins separately. But how can we automate testing of the whole end-to-end process? Here comes the FakeXrmEasy Pipeline Simulator to the rescue.

Please be aware that, due to the purpose of this article, plugins’ code (linked at the end of the text) has been simplified and doesn’t use many patterns and techniques that are useful and commonly used during plugins development and testing. The only reason for this code is to demonstrate how to work with the Pipeline Simulator tool, so please don’t treat it as an example of clean-architecture-like plugin implementation.

The first thing we need to do is create a new test project with the FakeXrmEasy NuGet package installed. For this article, I’ve used the XUnit testing framework, but you may also implement all the described techniques with different .NET testing frameworks (NUnit, MSTest, etc.). The next thing we will need is a base class for our test. Its main responsibility is to make the initial configuration of the Pipeline Simulator middleware, which gives us access to the Dataverse context and organization service mocks.

Again, for the purpose of this article, I’ve used a base class from the FakeXrmEasy documentation.

public class FakeXrmEasyPipelineTestsBase
{
    protected readonly IXrmFakedContext _context;
    protected readonly IOrganizationService _service;

    public FakeXrmEasyPipelineTestsBase()
    {
        _context = MiddlewareBuilder
                        .New()
                        .AddCrud()
                        .AddFakeMessageExecutors()
                        .AddPipelineSimulation()
                        .UsePipelineSimulation()
                        .UseCrud()
                        .UseMessages()
                        .SetLicense(FakeXrmEasyLicense.RPL_1_5)
                        .Build();

        _service = _context.GetAsyncOrganizationService();
    }
}

Ok. Let’s start implementing our test right now. The first thing we need to do is mock our context content. Because our plugins use Dataverse records (Account, Opportunity Owner, Opportunity Owner’s Manager), we need to make this data available for our test.

var ownerId = Guid.NewGuid();
var managerId = Guid.NewGuid();
var accountId = Guid.NewGuid();

var manager = new SystemUser()
{
  Id = managerId
};

var owner = new SystemUser()
{
  Id = ownerId,
  ParentSystemUserId = manager.ToEntityReference()
};

var account = new Account()
{
  Id = accountId,
  Revenue = new Money(20000000),
  OwnerId = owner.ToEntityReference(),         
};
_service.Create(manager);
_service.Create(owner);
_service.Create(account);

As you can see, no magic happens here. We are creating three in-memory objects representing required records and we are adding their data into a faked Dataverse context, using a service object created with FakeXrmEasy.

The next step is about registering testing plugins inside the faked Dataverse pipeline. Again, this is a very simple and self-explanatory code.

_context.RegisterPluginStep<OpportunityRatingPlugin>(new PluginStepDefinition()
{
  MessageName = "Create",
  EntityLogicalName = Opportunity.EntityLogicalName,
  Stage = ProcessingStepStage.Preoperation
});

_context.RegisterPluginStep<OpportunityNotificationsPlugin>(new PluginStepDefinition()
{
  MessageName = "Create",
  EntityLogicalName = Opportunity.EntityLogicalName,
  Stage = ProcessingStepStage.Postoperation
});

Right now, we have our data in place and plugins registered inside the in-memory context instance. The last step will be running the test itself and comparing its result with the expected values. We will trigger execution our registered plugins by creating the new Opportunity record. After the operation is completed, our expectation is to have a single email created where the Opportunity Owner’s Manager identifier is the “To” column value. Let’s take a look at how to implement it.

_service.Create(new Opportunity()
{
  CustomerId = account.ToEntityReference(),
  OwnerId = owner.ToEntityReference()
});

var result = _context.CreateQuery<Email>().FirstOrDefault();
Assert.Equal(managerId, result?.To?.FirstOrDefault()?.PartyId?.Id);

When plugins are executed successfully and without any errors, the assertion in the last line of the presented code should also be successful. The important thing worth mentioning is that, it is possible to debug our plugin code with Visual Studio during test execution, in case something is not going according to our expectations.

Ok. This will be all for today. I’m aware that this article describes only the tip of the iceberg of Pipline Simulator usage, and the possibilities provided by the mentioned tool. The initial idea was to let you know about its existence and provides a good entry point for further experiments and coding activities. Have fun!

All the code (tested plugins and tests) presented in this article may be found on our GitHub page.

Authors:

Piotr Gaszewski

Solution architect and OneDynamics head of engineering department.

Subscribe to oneDynamics

Sign up now to get access to the library of members-only issues.
Jamie Larson
Subscribe