4 min readRishi

SysTest in D365 F&O: Unit Tests That Survive a Platform Update

Most F&O customizations ship with a demo script and a prayer. Then a platform update lands, a Microsoft hotfix changes a base method you wrapped with Chain of Command, and the first person who notices is a controller during period close. SysTest is the cheapest insurance you can buy — if you treat it like production code, not like a checkbox on the ISV checklist.

What SysTest actually is

SysTest is the X++ unit-testing framework that runs inside the AOS. A test class extends SysTestCase (or a more specific base like SysTestCaseDataDependent), methods prefixed with test are discovered, and assertions live on the case object: this.assertEquals, this.assertTrue, this.assertNotNull. It is not a UI robot. It does not click forms. If your "test" needs a user to open Sales order details, you have an integration rehearsal, not a unit test.

That distinction matters because F&O's cost of a failed save is paid in finance hours, not in red CI. The tests that earn their keep are the ones that pin your logic: number-sequence fallbacks, CoC wrappers, custom posting validations, XDS policy predicates, SysOperation controllers. Leave the standard SalesTable insert path to Microsoft's own regression; you will never out-test the platform.

Isolate the data, or the suite will lie

The classic failure mode is a test that inserts a CustTable against whatever already lives in the UAT company, then asserts a count. After three sprints the count is wrong, someone "fixes" the assertion, and the test now documents the mess.

Use a dedicated test company or a fixture that creates and deletes in setUp / tearDown. Prefer ttsbegin / ttsabort when the engine allows it so leftover rows never escape. For tables with number sequences, call the sequence API explicitly with a known format rather than hoping the next value is SO-000123. If two tests share a unique index (item id, journal name, dimension combination), they will flake the first time they run in parallel on a build VM.

[SysTestMethod]
public void testOverCreditLimitBlocksConfirm()
{
    SalesTable sales = this.createOpenOrderAboveLimit();

    ttsbegin;
    sales.CreditLimitCheck = NoYes::Yes;
    this.assertFalse(sales.canConfirm(),
        "Orders over the customer limit must not confirm.");
    ttsabort;
}

The assertion message is not decoration. When this fails at 2 a.m. after a PU, the message is the only design doc the on-call engineer will read.

Skip the form, test the method

Form event handlers are awkward to instantiate in SysTest. If the real logic lives in clicked on a button, extract it to a class method the handler calls — then test the class. This is the same move as "skinny controllers" in any web stack, and it is the single refactor that makes CoC wrappers testable.

For SysOperation batch jobs, do not assert that a batch task was created. Instantiate the service class, call run(), and assert the table state. Batch infrastructure has its own timing; coupling to it turns a 200ms test into a 15-second wait.

What to pin against platform updates

A useful SysTest suite is a list of invariants you own:

  • CoC next still runs: if you wrap SalesLine.reserveNow, a test that reserves a known quantity and checks InventTrans is the tripwire when Microsoft changes the method signature or the call order.
  • Feature-management branches: if your code behaves differently when a Microsoft feature is enabled, have two tests, not one test that follows whichever flag UAT happens to have.
  • Data entities used by DMF or dual-write: insert through the entity and read the staging fields you map. Mapping drift is silent until a nightly job dumps 40,000 errors.

Run the suite on the build VM against the target PU before you apply it to UAT. A red suite on the build is a gift; a red period-close is not.

Keep the runtime honest

A 40-minute SysTest pass will be skipped. Cap each test in tens of milliseconds to low seconds. Mock external HTTP (custom services calling Azure) behind an interface. Do not start a full MRP in a unit test. Tag slow tests and run them nightly, not on every check-in.

If you only add one test this quarter, add the one that failed in production last time. SysTest does not need coverage theatre. It needs a growing list of things that already hurt once and must not hurt twice.

Keep reading

Newsletter

New posts, straight to your inbox

One email per post. No spam, no tracking pixels, unsubscribe anytime.

Comments

  • No comments yet. Be the first.