Skip to content

Assert expected exceptions in a Dataverse plugin scenario

Assert expected exceptions in a Dataverse plugin scenario

Section titled “Assert expected exceptions in a Dataverse plugin scenario”

A validation plugin is supposed to throw for some inputs. A red terminal alone does not tell you whether it rejected the record correctly or crashed for an unrelated reason. Declare the rejection you expect, then keep an accepted case beside it.

This how-to uses a business account without a VAT number. Success means the plugin raises the intended InvalidPluginExecutionException. With a VAT number present, success means it returns normally. The terminal also displays the plugin’s trace messages; these are not assertions. Each case is one scenario and one xg run invocation.

You need Windows, a working XrmGhost installation, and a .NET SDK that can build the example. Create a VatValidation folder and save these four files directly inside it:

The project targets net462, references the Dataverse SDK, and builds Contoso.HowTo.dll. The plugin’s relevant branch is:

var accountType = target.GetAttributeValue<OptionSetValue>("new_accounttype");
if (accountType == null || accountType.Value != 505480000)
return;
var vatNumber = target.GetAttributeValue<string>("new_vatnumber");
if (string.IsNullOrWhiteSpace(vatNumber))
{
tracing.Trace("Business account with no VAT number; blocking save.");
throw new InvalidPluginExecutionException(
"VAT number is required and must be valid for business accounts.");
}
tracing.Trace("Business account with a VAT number; allowing save.");

The full source also guards the message, Target and entity name. It handles Create only. new_accounttype, new_vatnumber and Business value 505480000 follow the Contoso demo convention; substitute your own schema in real code.

This deliberately narrow example checks presence, not VAT format, registration numbers or tax-authority records. Despite the shared business-facing exception wording, a non-empty but malformed VAT value is outside its coverage. It is not a replacement for a complete production validator.

In PowerShell, from that folder:

Terminal window
dotnet build Contoso.HowTo.csproj -c Debug -p:DebugType=full

The supplied vat-missing.json is a complete scenario, not an output transcript:

{
"scenarioName": "Business account without VAT must be rejected",
"pluginAssemblyName": "Contoso.HowTo.dll",
"pluginTypeName": "Contoso.HowTo.VatRequiredPlugin",
"executionContext": {
"messageName": "Create",
"primaryEntityName": "account",
"primaryEntityId": "00000000-0000-0000-0000-000000000001",
"stage": 20,
"mode": 0,
"inputParameters": {
"Target": {
"logicalName": "account",
"id": "00000000-0000-0000-0000-000000000001",
"attributes": {
"name": "Contoso Ltd",
"new_accounttype": { "value": 505480000 }
}
}
}
},
"expectedException": {
"typeName": "Microsoft.Xrm.Sdk.InvalidPluginExecutionException",
"messageContains": "VAT number is required and must be valid for business accounts."
}
}

The option-set value is an object with a value key, not a bare integer. The VAT attribute is absent. Stage 20 and mode 0 describe a synchronous pre-operation context. The assembly and type entries document the scenario; the command’s -a and -t select what actually executes.

expectedException.typeName must be the exact fully qualified name. The matcher walks the exception chain and requires the type and optional, case-sensitive messageContains substring to match on the same exception. A different exception, or no exception, fails the case. An empty type name matches nothing.

Terminal window
xg run -a bin\Debug\net462\Contoso.HowTo.dll -t Contoso.HowTo.VatRequiredPlugin -s vat-missing.json --data-source mock; $LASTEXITCODE

The captured run below reports Execution Succeeded! and Expected Exception Matched:: the rejection is the declared behaviour, not a failed check. Captured on Windows on 2026-10-29 with CLI 1.7.7 and Core 1.1.0. The final 0 is the exit code printed by $LASTEXITCODE.

Executing Plugin: Contoso.HowTo.VatRequiredPlugin
Assembly Path: bin\Debug\net462\Contoso.HowTo.dll
Scenario Loaded: Business account without VAT must be rejected
--- Plugin Execution Result ---
Entity Operations Summary:
Total Operations: 0
Successful: 0
Trace Log:
PLUGIN | 1ms | Business account with no VAT number; blocking save.
Performance:
[PERFORMANCE] Total: 2ms, Traces: 41 [Framework: 25, OperationTracker: 9, AssemblyResolver: 4, Plugi
n: 1, Diagnostic: 2]
Final Entity States: [PRO FEATURE - UPGRADE TO UNLOCK]
View final entity data, plugin modifications, and newly created records.
Get detailed insights into what your plugins actually changed.
$ Upgrade to Pro: https://www.xrmghost.tech/#pricing
Execution Succeeded!
Expected Exception Matched: Microsoft.Xrm.Sdk.InvalidPluginExecutionException (message contains: VAT
number is required and must be valid for business accounts.)
Output Parameters: {}
0
PS C:\src\contoso-demo\howto\vat-validation>

In vat-present.json, the context is the same, with one extra Target attribute:

"new_vatnumber": "IT12345678901"

There is no expectedException key.

Run it separately:

Terminal window
xg run -a bin\Debug\net462\Contoso.HowTo.dll -t Contoso.HowTo.VatRequiredPlugin -s vat-present.json --data-source mock; $LASTEXITCODE

The captured run completes normally with exit code 0, printed by $LASTEXITCODE. A plugin that rejects every account would pass the negative case but fail this one. Keeping both checks protects both sides of the rule.

Executing Plugin: Contoso.HowTo.VatRequiredPlugin
Assembly Path: bin\Debug\net462\Contoso.HowTo.dll
Scenario Loaded: Business account with VAT must be accepted
--- Plugin Execution Result ---
Entity Operations Summary:
Total Operations: 0
Successful: 0
Trace Log:
PLUGIN | 1ms | Business account with a VAT number; allowing save.
Performance:
[PERFORMANCE] Total: 1ms, Traces: 41 [Framework: 25, OperationTracker: 9, AssemblyResolver: 4, Plugi
n: 1, Diagnostic: 2]
Final Entity States: [PRO FEATURE - UPGRADE TO UNLOCK]
View final entity data, plugin modifications, and newly created records.
Get detailed insights into what your plugins actually changed.
$ Upgrade to Pro: https://www.xrmghost.tech/#pricing
Execution Succeeded!
Output Parameters: {}
0
PS C:\src\contoso-demo\howto\vat-validation>

These files are repeatable local checks, not a test-suite runner or a full Dataverse simulation. Assembly-loading failures remain failures; an expected exception cannot excuse broken setup. Keep your existing unit tests and validate the deployed behaviour in the target environment. The scenario reference covers matching details; Visual Studio debugging helps when the result is not what you intended.