Skip to content

ALM4Dataverse/Fake4Dataverse

Repository files navigation

Fake4Dataverse

An in-memory fake IOrganizationService and IOrganizationServiceAsync2 for unit testing Dataverse / Dynamics 365 applications — no live connection required.

Build & Test NuGet License: MIT

Features

Category Capabilities
CRUD Create, Retrieve, Update, Delete with auto-set fields (timestamps, owner, state, version number)
Service interfaces IOrganizationService and IOrganizationServiceAsync2 (async + cancellation token support)
QueryExpression Filtering (40+ condition operators), ordering, column projection, TopCount, paging with cookies
FetchXml FetchXml parsing and in-memory evaluation for the supported query surface, including aggregation (Count, Sum, Avg, Min, Max, GroupBy)
LinkEntity Inner/outer joins, nested joins, semi/anti joins (exists, in, any, not-any, not-all), link criteria, aliased attributes
Pipeline Pre-validation, pre-operation, and post-operation hooks (plugin-like)
Metadata Entity/attribute metadata store, validation on Create/Update, auto-discovery
Security Security roles, privilege enforcement, record sharing (Grant/Modify/Revoke access)
Execute handlers WhoAmI, SetState, Assign, Upsert, FetchXml/QueryExpression conversion requests, ExecuteMultiple, ExecuteTransaction, and more
Concurrency Optimistic concurrency (ConcurrencyBehavior.IfRowVersionMatches), atomic transactions with undo-log rollback
Calculated fields Calculated and rollup field definitions evaluated on Retrieve
Currency Exchange rates and auto-computed base currency amounts
Activity parties ActivityParty entity support for from/to/cc/bcc fields
Binary attributes Image and file column storage
Seeding Bulk insert, JSON seeding, EntityBuilder fluent API
Snapshots TakeSnapshot() / RestoreSnapshot() / Scope() for test isolation
Time control FakeClock for deterministic date/time testing
Operation log Records all service calls for post-hoc assertions
Configuration FakeOrganizationServiceOptions with Strict/Lenient presets
Multi-user Multiple FakeOrganizationService sessions against the same FakeDataverseEnvironment
Multi-target .NET Framework 4.6.2 and .NET 10

Built-in query conversion handlers are registered automatically: FetchXmlToQueryExpressionRequest converts supported non-aggregate FetchXml, and QueryExpressionToFetchXmlRequest serializes the supported QueryExpression surface without silently degrading unsupported operators or join types.

Installation

dotnet add package Fake4Dataverse

Quick Start

Basic CRUD

var env = new FakeDataverseEnvironment();
var service = env.CreateOrganizationService();

var id = service.Create(new Entity("account") { ["name"] = "Contoso" });

var retrieved = service.Retrieve("account", id, new ColumnSet("name"));
Assert.Equal("Contoso", retrieved["name"]);

service.Update(new Entity("account") { Id = id, ["name"] = "Contoso Ltd." });
service.Delete("account", id);

Async Interface (IOrganizationServiceAsync2)

using Microsoft.PowerPlatform.Dataverse.Client;
using System.Threading;

var env = new FakeDataverseEnvironment();
var service = env.CreateOrganizationService();
var asyncService = (IOrganizationServiceAsync2)service;

var id = await asyncService.CreateAsync(
    new Entity("account") { ["name"] = "Contoso" },
    CancellationToken.None);

var account = await asyncService.RetrieveAsync(
    "account", id, new ColumnSet("name"),
    CancellationToken.None);

Test a Real Plugin

[Fact]
public void Create_ExecutesRegisteredPlugin()
{
    var env = new FakeDataverseEnvironment();
    var service = env.CreateOrganizationService();
    env.Pipeline.RegisterStep("Create", PipelineStage.PreOperation, "account", new PrefixNamePlugin());

    var id = service.Create(new Entity("account") { ["name"] = "Contoso" });
    var account = service.Retrieve("account", id, new ColumnSet("name"));

    Assert.Equal("PLUGIN: Contoso", account.GetAttributeValue<string>("name"));
}

private sealed class PrefixNamePlugin : IPlugin
{
    public void Execute(IServiceProvider serviceProvider)
    {
        var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext))!;
        var target = (Entity)context.InputParameters["Target"];
        target["name"] = $"PLUGIN: {target.GetAttributeValue<string>("name")}";
    }
}

Configuration

FakeOrganizationServiceOptions controls auto-set behaviors, validation, security, and pipeline. Use built-in presets for common setups:

var strict  = new FakeDataverseEnvironment(FakeOrganizationServiceOptions.Strict);   // metadata validation + security on
var lenient = new FakeDataverseEnvironment(FakeOrganizationServiceOptions.Lenient);   // everything off — full manual control
var strictService  = strict.CreateOrganizationService();
var lenientService = lenient.CreateOrganizationService();

See the Configuration Reference for all options.

Companion Packages

Package Description
Fake4Dataverse.FakeItEasy Wraps the fake service as a FakeItEasy Fake<IOrganizationService>
Fake4Dataverse.Moq Wraps the fake service as a Moq Mock<IOrganizationService>
Fake4Dataverse.AwesomeAssertions Should().HaveCreated(…) style assertions via AwesomeAssertions
Fake4Dataverse.FluentAssertions Should().HaveCreated(…) style assertions via FluentAssertions
Fake4Dataverse.Shouldly ShouldHaveCreated(…) style assertions via Shouldly
Fake4Dataverse.Spkl Auto-register plugins from SPKL [CrmPluginRegistration] attributes

Install any adapter alongside the core package:

dotnet add package Fake4Dataverse.AwesomeAssertions

Documentation

Guides

Reference

Samples

The samples/ directory contains runnable examples with matching test projects:

  • AccountService — production-style service using IOrganizationService, with unit tests.
  • Plugin — real IPlugin implementation with pipeline-based tests.

License

MIT

About

A .NET library that provides a fake implementation of Microsoft Dataverse for testing - developed to meet the advanced needs of Rnwood Dataverse PowerShell module

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages