Dynamics365.BusinessCentral.Testing
2.0.0
dotnet add package Dynamics365.BusinessCentral.Testing --version 2.0.0
NuGet\Install-Package Dynamics365.BusinessCentral.Testing -Version 2.0.0
<PackageReference Include="Dynamics365.BusinessCentral.Testing" Version="2.0.0" />
<PackageVersion Include="Dynamics365.BusinessCentral.Testing" Version="2.0.0" />
<PackageReference Include="Dynamics365.BusinessCentral.Testing" />
paket add Dynamics365.BusinessCentral.Testing --version 2.0.0
#r "nuget: Dynamics365.BusinessCentral.Testing, 2.0.0"
#:package Dynamics365.BusinessCentral.Testing@2.0.0
#addin nuget:?package=Dynamics365.BusinessCentral.Testing&version=2.0.0
#tool nuget:?package=Dynamics365.BusinessCentral.Testing&version=2.0.0
Dynamics365.BusinessCentral.Testing
Test doubles for Dynamics365.BusinessCentral.
The highest-risk part of using an OData client is the OData it generates — filter
rendering, $select field names, casing, paging. Mocking IBusinessCentralClient can't
test any of that: a mock verifies that a query happened, not what it asked for.
FakeBusinessCentral takes the opposite approach: it runs a real
BusinessCentralClient over a scripted transport. URL building, filter rendering, paging,
retry and deserialization execute exactly as in production; you script the HTTP responses
and assert on the requests that were actually produced.
using var bc = new FakeBusinessCentral();
bc.EnqueuePage(new Item { No = "X", Description = "Pump" });
var items = await bc.Client.QueryAsync<Item>("items",
Filter.Equals("no", "X"), select: ["no", "description"]);
Assert.Equal(
"/Company('TEST')/items?$filter=no eq 'X'&$select=no,description",
bc.Requests.Single().DecodedPathAndQuery);
Scripting
Responses are consumed in order, one per request. Token acquisition is answered
automatically (and counted in TokenRequestCount, not recorded), so tests never script
auth. An unscripted request throws with a message naming it — no silent empty results.
bc.EnqueuePage(rows) // {"value":[...]}
bc.EnqueuePage(page1, nextLink: "page2") // server-driven paging
bc.EnqueuePage(rows, totalCount: 42) // @odata.count
bc.EnqueueEntity(entity) // GET by key / write echo
bc.EnqueueNoContent() // 204 on a write
bc.EnqueueError(HttpStatusCode.TooManyRequests,
retryAfter: TimeSpan.FromSeconds(5)) // typed exception paths
bc.EnqueueNetworkFailure() // BusinessCentralConnectionException
bc.Enqueue(req => ...) // anything else
Failure scripting means catch branches are testable without constructing exception types
by hand — the real BusinessCentralExceptionFactory maps the scripted status code to the
right BusinessCentralException subtype.
What this can and cannot prove
FakeBusinessCentral proves your half of the contract: that your code produces the
OData you intend. It cannot prove Business Central's half — that the server accepts it.
A real example from a production consumer: a test asserting
$filter=no in ('EBH100','EBT200') passed, while the live tenant rejected that exact
filter with BadRequest_MethodNotImplemented, because BC does not support the in
operator below schema version 2.1. The fake answers whatever it is scripted to answer.
Treat wire-level compatibility as a separate concern: verify operators against a live tenant once, then let these tests guard against regressions in what you generate.
BusinessCentralMetadata — the one check the fake cannot do
Because the fake answers what it is scripted to answer, it cannot tell you whether a derived
$select names a real column. The fluent builder projects from your entity type's settable
scalar properties, and a property mapping to no Business Central column fails the whole
query with a 400. Nothing else in a normal test suite catches that: mocks do not validate
$select either.
This does, against a live (non-production) tenant:
[Fact]
public async Task Every_entity_projection_resolves()
=> await BusinessCentralMetadata.AssertProjectionsResolveAsync(
_client, typeof(Item).Assembly);
It derives the $select for every [BusinessCentralEntity] type in the assembly and throws
listing every unresolved name, so one run tells you everything instead of one 400 at a
time. ValidateAsync returns a BusinessCentralProjectionReport rather than throwing, for
callers that want to log or filter.
Run it on every build, not once at upgrade — the failure is introduced by adding a property, an edit nobody associates with a query breaking.
Parse and Validate are pure: hand them a canned $metadata document and a list of types to
test your own tooling around this without a tenant.
Assertions
Requests records every data request in order: Method, Uri, Body, PathAndQuery
(as sent on the wire) and DecodedPathAndQuery (percent-decoding undone, for readable
assertions).
Configuration
Defaults: company TEST, base URL https://bc.test, instant deterministic retries.
Override anything via the constructor:
using var bc = new FakeBusinessCentral(o => o.Company = "CRONUS AG");
For DI-level tests, wire bc.Handler in as the primary handler instead of using
bc.Client:
services.AddBusinessCentral(...);
services.AddHttpClient(BusinessCentralHttpClients.Client)
.ConfigurePrimaryHttpMessageHandler(() => bc.Handler);
Versioned in lockstep with the main package. Full documentation: https://github.com/KralGmbh/Dynamics365-BusinessCentral
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Dynamics365.BusinessCentral (>= 2.0.0)
-
net8.0
- Dynamics365.BusinessCentral (>= 2.0.0)
-
net9.0
- Dynamics365.BusinessCentral (>= 2.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.0 | 144 | 8/15/2026 |
| 2.0.0-rc.1 | 146 | 8/6/2026 |
| 2.0.0-alpha.7 | 92 | 8/4/2026 |
| 2.0.0-alpha.6 | 59 | 7/30/2026 |
| 2.0.0-alpha.5 | 55 | 7/30/2026 |
| 2.0.0-alpha.4 | 53 | 7/30/2026 |
| 2.0.0-alpha.3 | 55 | 7/30/2026 |
Test doubles for Dynamics365.BusinessCentral. Versioned in lockstep with the
main package — see its release notes and changelog:
https://github.com/KralGmbh/Dynamics365-BusinessCentral/blob/master/CHANGELOG.md