AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Bc Test Codeunit Generator

skill-fernandoartalf-al-copilot-skills-collection-bc-test-codeunit-generator · by fernandoartalf

Generates test codeunits (Subtype = Test) for Business Central extensions that verify existing module functionality including page navigation, codeunit logic, setup validation, and data operations. Creates test codeunits with [Test] attribute methods following Given/When/Then naming, handler methods ([MessageHandler], [ConfirmHandler], [PageHandler], [ModalPageHandler], [HyperlinkHandler], [SendN…

No reviews yet
0 installs
43 views
0.0% view→install

Install

$ agentstack add skill-fernandoartalf-al-copilot-skills-collection-bc-test-codeunit-generator

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-fernandoartalf-al-copilot-skills-collection-bc-test-codeunit-generator)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Bc Test Codeunit Generator? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Business Central Test Codeunit Generator

Generates production-ready test codeunits and test runner codeunits for Business Central extensions. Tests existing module functionality including page navigation, codeunit operations, setup validation, and data integrity.

Overview

Test codeunits (Subtype = Test) contain test methods that verify module behavior. Each test method runs in its own database transaction (rolled back after execution by default). Test runner codeunits (Subtype = TestRunner) manage execution of test codeunits and integrate with test management frameworks.

This skill generates:

  • Test codeunits with [Test] attribute methods
  • Handler methods for UI automation ([MessageHandler], [ConfirmHandler], [PageHandler], [ModalPageHandler])
  • Test runner codeunits with OnBeforeTestRun/OnAfterTestRun triggers
  • Integration with standard BC test libraries (Library Assert, Library - Sales, Library - ERM, Library - Inventory, Library - Random)
  • Given/When/Then test naming convention
  • Test data factory patterns

Complete examples and patterns: [references/test-examples.md](references/test-examples.md)

Prerequisites

  • AL workspace with established object ID range and prefix
  • Test project with separate app.json referencing the App project as a dependency
  • Test framework dependencies declared in test project app.json:
  • dd0be2ea-f733-4d65-bb34-a28f4624fb14 — Library Assert
  • e7320ebb-08b3-4e1e-8c4b-37ae5bb1f994 — Library Variable Storage
  • 5d86850b-0d76-4eca-bd7b-951ad998e997 — Any (test runner)
  • 9856ae4f-d1a7-46ef-89bb-6ef056398228 — System Application Test Library
  • Existing module objects (tables, pages, codeunits) to test

Test Method Types

| Attribute | Purpose | Behavior | |-----------|---------|----------| | [Test] | Main test method | Runs as a test case, reported as pass/fail | | [MessageHandler] | Handles Message() calls | Receives message text, validates with Library Assert | | [ConfirmHandler] | Handles Confirm() dialogs | Returns reply boolean, validates question text | | [PageHandler] | Handles Page.Run() on non-modal pages | Receives page instance for interaction | | [ModalPageHandler] | Handles Page.RunModal() on modal pages | Receives page instance, simulates OK/Cancel | | [HyperlinkHandler] | Handles Hyperlink() calls | Receives URL string | | [SendNotificationHandler] | Handles Notification.Send() | Receives notification, validates message | | [RecallNotificationHandler] | Handles Notification.Recall() | Receives notification being recalled | | [RequestPageHandler] | Handles report request pages | Sets report parameters | | [ReportHandler] | Handles Report.Run() | Receives report instance | | [FilterPageHandler] | Handles filter page dialogs | Sets filter values | | [StrMenuHandler] | Handles StrMenu() calls | Sets menu choice | | [SessionSettingsHandler] | Handles session settings update | Controls session restart | | [Normal] | Helper method | Regular procedure for test setup/utilities |

Quick Start — Test Codeunit

codeunit [TestID] "[Prefix] [Feature] Tests"
{
  Subtype = Test;

  var
    Assert: Codeunit "Library Assert";
    LibraryVariableStorage: Codeunit "Library Variable Storage";
    IsInitialized: Boolean;

  local procedure Initialize()
  begin
    LibraryVariableStorage.Clear();
    if IsInitialized then
      exit;

    // One-time test setup
    IsInitialized := true;
    Commit();
  end;

  [Test]
  procedure GivenSetup_WhenOpenSetupPage_ThenPageOpens()
  var
    SetupPage: TestPage "[Prefix] Module Setup";
  begin
    // [GIVEN] Module is installed
    Initialize();

    // [WHEN] Setup page is opened
    SetupPage.OpenEdit();

    // [THEN] Page opens without error
    SetupPage.Close();
  end;
}

Quick Start — Test Runner Codeunit

codeunit [RunnerID] "[Prefix] Test Runner"
{
  Subtype = TestRunner;
  TestIsolation = Codeunit;

  trigger OnRun()
  begin
    // Run test codeunits
    Codeunit.Run(Codeunit::"[Prefix] [Feature] Tests");
  end;

  trigger OnBeforeTestRun(
    CodeunitId: Integer;
    CodeunitName: Text;
    FunctionName: Text;
    Permissions: TestPermissions): Boolean
  begin
    exit(true);
  end;

  trigger OnAfterTestRun(
    CodeunitId: Integer;
    CodeunitName: Text;
    FunctionName: Text;
    Permissions: TestPermissions;
    IsSuccess: Boolean)
  begin
  end;
}

Test Naming Convention

Follow Given/When/Then pattern:

[Given]_When_Then

Examples:

  • GivenValidCustomer_WhenCreatingOrder_ThenOrderIsCreated
  • GivenSetupRecord_WhenOpenSetupPage_ThenFieldsAreVisible
  • GivenBlankSetup_WhenRunInstall_ThenSetupRecordExists
  • GivenStatAccount_WhenPostJournal_ThenLedgerEntryCreated

Test Categories

1. Setup and Configuration Tests

Verify setup table initialization, default values, and page navigation:

[Test]
procedure GivenFirstInstall_WhenOpenSetup_ThenRecordCreated()
var
  Setup: Record "[Prefix] Module Setup";
  SetupPage: TestPage "[Prefix] Module Setup";
begin
  Initialize();

  // [GIVEN] No setup record
  Setup.DeleteAll();

  // [WHEN] Open setup page
  SetupPage.OpenEdit();

  // [THEN] Setup record auto-created
  Assert.IsTrue(Setup.Get(), 'Setup record should be created on page open');
  SetupPage.Close();
end;

2. Page Navigation Tests

Verify pages open, fields display correctly, and actions work:

[Test]
procedure GivenModule_WhenOpenListPage_ThenPageOpens()
var
  ListPage: TestPage "[Prefix] Record List";
begin
  Initialize();

  // [WHEN] Open list page
  ListPage.OpenView();

  // [THEN] Page opens without error
  ListPage.Close();
end;

[Test]
procedure GivenRecordExists_WhenOpenCardPage_ThenFieldsPopulated()
var
  MyRecord: Record "[Prefix] My Record";
  CardPage: TestPage "[Prefix] My Record Card";
begin
  Initialize();

  // [GIVEN] Record exists
  CreateTestRecord(MyRecord);

  // [WHEN] Open card for record
  CardPage.OpenEdit();
  CardPage.GoToRecord(MyRecord);

  // [THEN] Fields are populated
  Assert.AreEqual(MyRecord."No.", CardPage."No.".Value(), 'No. field should show record number');
  CardPage.Close();
end;

3. Codeunit Logic Tests

Verify business logic in codeunits:

[Test]
procedure GivenValidData_WhenRunProcess_ThenResultIsCorrect()
var
  ProcessMgmt: Codeunit "[Prefix] Process Management";
  InputRecord: Record "[Prefix] Input Table";
  ResultRecord: Record "[Prefix] Result Table";
begin
  Initialize();

  // [GIVEN] Valid input data
  CreateTestInput(InputRecord);

  // [WHEN] Run processing
  ProcessMgmt.ProcessRecord(InputRecord);

  // [THEN] Result record exists with correct values
  ResultRecord.SetRange("Source Entry No.", InputRecord."Entry No.");
  Assert.RecordIsNotEmpty(ResultRecord);
end;

4. Error Validation Tests

Verify proper error handling:

[Test]
procedure GivenInvalidData_WhenValidate_ThenErrorRaised()
var
  MyRecord: Record "[Prefix] My Record";
begin
  Initialize();

  // [GIVEN] Record with invalid data
  MyRecord.Init();

  // [WHEN/THEN] Validation raises error
  asserterror MyRecord.Validate("Required Field", '');
  Assert.ExpectedError('Required Field must have a value');
end;

5. Event Subscriber Tests

Verify event subscribers fire and produce expected results:

[Test]
procedure GivenSubscriber_WhenEventFires_ThenHandlerExecutes()
var
  SourceRecord: Record "Source Table";
  ExtendedField: Record "[Prefix] Extended Table";
begin
  Initialize();

  // [GIVEN] Source record
  CreateSourceRecord(SourceRecord);

  // [WHEN] Action that triggers event
  SourceRecord.Modify(true);

  // [THEN] Subscriber should have updated extended data
  ExtendedField.SetRange("Source No.", SourceRecord."No.");
  Assert.RecordIsNotEmpty(ExtendedField);
end;

Handler Method Patterns

MessageHandler

[MessageHandler]
procedure MessageHandler(Message: Text[1024])
begin
  // Option A: Verify specific message
  Assert.ExpectedMessage(LibraryVariableStorage.DequeueText(), Message);

  // Option B: Just suppress (do nothing)
end;

ConfirmHandler

[ConfirmHandler]
procedure ConfirmHandler(Question: Text[1024]; var Reply: Boolean)
begin
  Reply := LibraryVariableStorage.DequeueBoolean();
end;

ModalPageHandler

[ModalPageHandler]
procedure ModalPageHandler(var TestPage: TestPage "Target Page")
begin
  // Simulate user interaction
  TestPage."Field Name".SetValue(LibraryVariableStorage.DequeueText());
  TestPage.OK().Invoke();
end;

PageHandler

[PageHandler]
procedure PageHandler(var TestPage: TestPage "Target Page")
begin
  // Verify page content
  Assert.AreEqual(
    LibraryVariableStorage.DequeueText(),
    TestPage."Field Name".Value(),
    'Field should match expected value');
  TestPage.Close();
end;

TestPage Methods Reference

| Method | Purpose | |--------|---------| | OpenNew() | Open page in insert mode | | OpenEdit() | Open page in edit mode | | OpenView() | Open page in read-only mode | | Close() | Close the page | | GoToRecord(Record) | Navigate to a specific record | | GoToKey(Values...) | Navigate to record by primary key | | First() / Last() / Next() / Previous() | Navigate records on list pages | | New() | Insert new record on list page | | "FieldName".SetValue(Value) | Set a field value | | "FieldName".Value() | Read field display value | | "FieldName".AssistEdit() | Trigger AssistEdit | | "FieldName".Lookup() | Trigger field lookup | | "FieldName".DrillDown() | Trigger drilldown | | "ActionName".Invoke() | Run a page action | | OK().Invoke() | Click OK button | | Cancel().Invoke() | Click Cancel button |

Test Runner Codeunit

Purpose

Test runner codeunits (Subtype = TestRunner) manage execution of test codeunits and integrate with test management/reporting frameworks.

Properties

| Property | Values | Description | |----------|--------|-------------| | Subtype | TestRunner | Marks codeunit as a test runner | | TestIsolation | Disabled, Codeunit, Function | Controls transaction rollback scope |

  • Disabled: No automatic rollback
  • Codeunit: Roll back changes after each test codeunit completes
  • Function: Roll back changes after each test method completes (most isolation)

Triggers

| Trigger | Parameters | Purpose | |---------|------------|---------| | OnRun | None | Entry point — run test codeunits here | | OnBeforeTestRun | CodeunitId, CodeunitName, FunctionName, PermissionsBoolean | Pre-test hook; return false to skip test | | OnAfterTestRun | CodeunitId, CodeunitName, FunctionName, Permissions, IsSuccess | Post-test hook; log results |

Important: OnBeforeTestRun and OnAfterTestRun always run in their own transactions regardless of TestIsolation or TransactionModel settings.

When FunctionName is empty in OnBeforeTestRun/OnAfterTestRun, it refers to the OnRun trigger itself.

Updating an Existing Test Runner

When adding a new test codeunit to an existing test runner:

  1. Open the test runner codeunit
  2. Add Codeunit.Run(Codeunit::"[Prefix] New Test Codeunit"); in the OnRun trigger
  3. Keep consistent order (group by feature area)

Workflow

Step 1: Analyze the Module

Before generating tests, identify what to test:

  1. Search the workspace for all objects in the module (tables, pages, codeunits, reports)
  2. Read each object to understand its structure and logic
  3. Identify testable elements:
  • Page opens and navigation (card, list, API pages)
  • Setup page initialization
  • Codeunit procedures and business logic
  • Table validations and triggers
  • Event subscribers
  • Error handling paths

Step 2: Determine Test Codeunit Structure

  • One test codeunit per feature area or per source codeunit
  • Name: [Prefix] [Feature] Tests (max 30 characters)
  • ID: Next available in the test ID range

Step 3: Create Test Codeunit

  1. Create the file in the test project folder
  2. Add Subtype = Test property
  3. Declare standard test library variables (Assert, LibraryVariableStorage, etc.)
  4. Add Initialize() procedure for shared setup
  5. Create [Test] methods following Given/When/Then naming
  6. Add handler methods for any UI interactions
  7. Add helper procedures for test data creation

Step 4: Create or Update Test Runner

If no test runner exists:

  1. Create new codeunit with Subtype = TestRunner
  2. Set TestIsolation = Codeunit
  3. Add Codeunit.Run() calls for each test codeunit in OnRun
  4. Implement OnBeforeTestRun returning true
  5. Implement OnAfterTestRun (empty or with logging)

If test runner exists:

  1. Open existing test runner
  2. Add new Codeunit.Run(Codeunit::"[Prefix] New Tests"); to OnRun

Step 5: Build and Validate

  1. Build the test project to verify compilation
  2. Run tests via test runner or VS Code Test Explorer
  3. Verify all tests pass

Design Guidelines

  • One assertion per test when practical — makes failures easier to diagnose
  • Independent tests — each test should set up its own data, not depend on other tests
  • Use Initialize() pattern for expensive one-time setup shared across tests
  • Commit() after Initialize() — required so test transactions work correctly
  • Use asserterror for negative tests — validates expected errors
  • Prefer Library Assert methods over raw Error() comparisons
  • Use LibraryVariableStorage to pass values between test methods and handlers
  • Enqueue before invoke — always enqueue values in LibraryVariableStorage before triggering actions that invoke handlers
  • No hardcoded IDs — use LibraryRandom for quantities and amounts
  • Use standard libraries — prefer Library - Sales, Library - ERM for creating standard BC records
  • Clean descriptive names — test names should describe the scenario without reading the code
  • Test both positive and negative paths — verify success cases AND error handling

File Naming Convention

  • Test codeunit: [Prefix][Feature]Tests.Codeunit.al
  • Test runner: [Prefix]TestRunner.Codeunit.al

Examples:

  • BCSStatAccTests.Codeunit.al / BCSTestRunner.Codeunit.al
  • BCSSetupTests.Codeunit.al

Place in: test project src/[Feature]Tests/ or test/[Feature]/ folder.

Checklist

Before completing test codeunit generation:

  • [ ] Subtype = Test set on test codeunit
  • [ ] [Test] attribute on each test method
  • [ ] Given/When/Then naming convention followed
  • [ ] Initialize() procedure with IsInitialized guard and Commit()
  • [ ] Library Assert used for all assertions
  • [ ] Handler methods declared for any UI interactions triggered by tests
  • [ ] LibraryVariableStorage used for passing data to handlers
  • [ ] Values enqueued before actions that invoke handlers
  • [ ] Test data created within each test (independent tests)
  • [ ] Both positive and error paths tested
  • [ ] Test runner codeunit created or updated with new test codeunit
  • [ ] TestIsolation = Codeunit set on test runner
  • [ ] OnBeforeTestRun returns true
  • [ ] File follows naming convention

External References

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.