Skip to content
Development
Skill

/skill-testing

AL test development patterns for Business Central. Use when creating test codeunits, writing Given/When/Then test procedures, using Library Assert, configuring test projects, or implementing TDD workflows.

BOOST
From plugin
aldc-al-development-collection
11237 skills28 agents11 commands4 MCP
Install
$ npx -y skills add javiarmesto/ALDC-AL-Development-Collection --skill skill-testing --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/skill-testing

Context preview

The summary Claude sees to decide when to auto-load this skill.

AL test development patterns for Business Central. Use when creating test codeunits, writing Given/When/Then test procedures, using Library Assert, configuring test projects, or implementing TDD workflows.

SKILL.md

skill-testing.SKILL.md
name: skill-testing
description: "AL test development patterns for Business Central. Use when creating test codeunits, writing Given/When/Then test procedures, using Library Assert, configuring test projects, or implementing TDD workflows."

Skill: AL Testing & Test Strategy

Purpose

Design test strategies, implement Given/When/Then test patterns, build reusable library codeunits, test Copilot features with AI Test Toolkit, and integrate testing into the conductor's TDD cycle for AL Business Central extensions.

When to Load

This skill should be loaded when:

  • A test strategy or test plan needs to be designed for a feature
  • Test codeunits need to be created (unit, integration, UI)
  • The conductor runs a TDD cycle (RED → GREEN → REFACTOR)
  • Copilot/AI-powered features need testing with AI Test Toolkit
  • Test data builders or library codeunits need to be created
  • Test failures need analysis or coverage gaps need to be addressed

Core Patterns

When BC29 test lifecycle instrumentation is needed, read the test section of [AL18 capability checks](../skill-migrate/references/al18-capabilities.md). `TestHandlers` / `ITestHandler` require target declaration checks and do not replace the approved runner. Test-library versions need not equal the app version: check dependency identity, minimum version, runtime and APIs. Unknown compatibility limits the affected validation; it does not automatically block drafting the spec.

**Executed or only compiled.** A project declares how its tests run in `aldc.yaml → testing.runner` (`ALDC_TEST_RUNNER` overrides it). With a runner, its output is the runtime-test evidence; a failing test fails the phase. Without one, tests are compiled, not executed: report them as `not-run`, never as passing.

Pattern 1: Given/When/Then Test Structure

Every test follows GWT with explicit comments and a descriptive name:

codeunit 50100 "Discount Calculation Tests"
{
    Subtype = Test;
    TestPermissions = Disabled;   // runs as SUPER; not a rollback setting

    var
        Assert: Codeunit Assert;
        LibrarySales: Codeunit "Library - Sales";
        LibraryRandom: Codeunit "Library - Random";
        IsInitialized: Boolean;

    [Test]
    procedure CalculateLineDiscount_VolumeOver100_Applies15Percent()
    var
        SalesLine: Record "Sales Line";
        DiscountMgt: Codeunit "Contoso Discount Management";
        Result: Decimal;
    begin
        // [SCENARIO] Volume discount is correctly applied for quantities ≥ 100
        Initialize();

        // [GIVEN] A sales line with quantity 100 and unit price 10
        CreateSalesLineWithQty(SalesLine, 100, 10);

        // [GIVEN] Volume discount setup: 100+ units → 15%
        CreateVolumeDiscountSetup(100, 15);

        // [WHEN] Discount is calculated
        Result := DiscountMgt.CalculateLineDiscount(SalesLine);

        // [THEN] Discount percentage is 15%
        Assert.AreEqual(15, Result, 'Volume discount not applied for qty >= 100');

        // [THEN] Line amount reflects the discount
        Assert.AreEqual(850, SalesLine."Line Amount",
            'Line amount should be 100 * 10 * (1 - 0.15) = 850');
    end;

    local procedure Initialize()
    begin
        if IsInitialized then
            exit;
        // One-time setup: number series, general setup, etc.
        IsInitialized := true;
    end;
}

**Naming convention**: `Action_Condition_ExpectedOutcome` — reads as a sentence.

Pattern 2: Library Codeunit (Reusable Test Helpers)

Encapsulate test data creation in library codeunits — one per domain:

codeunit 50200 "Library - Contoso Sales"
{
    /// Creates a customer with standard defaults for testing.
    procedure CreateCustomer(var Customer: Record Customer)
    var
        LibrarySales: Codeunit "Library - Sales";
    begin
        LibrarySales.CreateCustomer(Customer);   // standard BC library
        Customer."Credit Limit (LCY)" := 100000;
        Customer.Modify();
    end;

    /// Creates a released sales order with one line.
    procedure CreateReleasedSalesOrder(
        var SalesHeader: Record "Sales Header";
        CustomerNo: Code[20];
        ItemNo: Code[20];
        Qty: Decimal)
    var
        SalesLine: Record "Sales Line";
        LibrarySales: Codeunit "Library - Sales";
    begin
        LibrarySales.CreateSalesHeader(
            SalesHeader, SalesHeader."Document Type"::Order, CustomerNo);
        LibrarySales.CreateSalesLine(
            SalesLine, SalesHeader, SalesLine.Type::Item, ItemNo, Qty);
        LibrarySales.ReleaseSalesDocument(SalesHeader);
    end;
}

**Rules:**

  • Always delegate to standard BC library codeunits (`Library - Sales`, `Library - Inventory`, `Library - ERM`, `Library - Random`) for base data creation
  • Add extension-specific fields on top
  • Keep helpers stateless — no global variables in library codeunits
  • Place in `Test/src/Libraries/` folder

Pattern 3: Test Data Builder (Fluent API)

For complex test scenarios with many optional parameters:

codeunit 50201 "Sales Order Builder"
{
    var
        SalesHeader: Record "Sales Header";
        LineCount: Integer;

    procedure Create(): Codeunit "Sales Order Builder"
    begin
        SalesHeader.Init();
        SalesHeader."Document Type" := SalesHeader."Document Type"::Order;
        SalesHeader.Insert(true);
        exit(this);
    end;

    procedure WithCustomer(CustomerNo: Code[20]): Codeunit "Sales Order Builder"
    begin
        SalesHeader.Validate("Sell-to Customer No.", CustomerNo);
        SalesHeader.Modify(true);
        exit(this);
    end;

    procedure WithLine(ItemNo: Code[20]; Qty: Decimal): Codeunit "Sales Order Builder"
    var
        SalesLine: Record "Sales Line";
    begin
        LineCount += 10000;
        SalesLine.Init();
        SalesLine."Document Type" := SalesHeader."Document Type";
        SalesLine."Document No." := SalesHeader."No.";
        SalesLine."Line No." := LineCount;
        SalesLine.Ins
Read more
Ships withaldc-al-development-collection

Skills-based, spec-driven, TDD-orchestrated development framework for Microsoft Dynamics 365 Business Central. Engineering systems, visibly reasoned.

Get the whole plugin