Skip to content

tcunit runner

Port: TestRunner Module: tckit.adapters.test_runners.tcunit_runner.TcUnitRunner Requires: the TcUnit library installed in the system library repository (distributor www.tcunit.org). Without it, projects that reference TcUnit will fail to build with "library not found".

End-to-end TcUnit test driver: orchestrates a run against a target runtime, polls for completion via ADS, and parses TcUnit's JUnit-shaped XML output into a structured TestResults.

Configuration

{ "test_runner": "tcunit" }

The runner reads no env config of its own; it picks up BRIDGE_URL, PLC_PROJECT_NAME, and TARGET_AMS_ID from the shared environment along with the rest of TcKit, and runs against the solution open in the attached XAE. The optional TCKIT_TEST_RUN_TIMEOUT env var (default 180 seconds) caps the HTTP timeout on the /tcunit-run call when the test suites take a long time to complete.

xUnit publisher output

TcUnit ships an xUnit publisher gated on GVL_Param_TcUnit.xUnitEnablePublish (a library parameter on the TcUnit placeholder). When TRUE, the publisher writes JUnit-style XML to the path on GVL_Param_TcUnit.xUnitFilePath, which defaults to %TC_BOOTPRJPATH%tcunit_xunit_testresults.xml — i.e. C:\TwinCAT\3.1\Boot\Plc\Port_851\tcunit_xunit_testresults.xml for a PLC running at the standard runtime port.

Override the publisher flag through the writer port:

writer.add_library_placeholder(
    "TestsPlc", "TcUnit", "TcUnit",
    distributor="www.tcunit.org",
    parameters={"xUnitEnablePublish": "TRUE"},
)

Without the override, suites still run and the runner state machine completes correctly, but no XML lands and /results has nothing to parse. The bridge's /tcunit-run reports xml_published=false in that case so callers can tell why the file isn't there.

If the consumer also overrides xUnitFilePath, pass the resolved path back to /results explicitly via XmlPath — the bridge does not introspect the runtime to discover overrides today.

Bridge routes

The adapter is a thin route-caller over three Windows bridge endpoints (all POST):

Route Script Purpose
/tcunit-run Invoke-TcUnitRun.ps1 Delete stale XML, ensure runtime in Run mode, poll TcUnit.G_TestRunner.bTestSuitesFinished via ADS, wait for the XML to land, return live summary counters.
/results Get-TcUnitResults.ps1 Read the XML at the resolved path, parse <testsuites>/<testsuite>/<testcase>/<failure> into the structured shape below.
/runtime Invoke-TcRuntime.ps1 Mode-aware runtime transition. Mode = "Run" is what the test run uses; start_runtime also reaches it.

Stale XML is deleted at the start of every run; the script records a start epoch and verifies the new XML's mtime is strictly greater before returning. This is the only safeguard against reading yesterday's results.

TestResults JSON shape

{
  "suites": [
    {
      "name": "FB_Adder_Suite",
      "tests": [
        {
          "name": "Adds_TwoPositives",
          "passed": true,
          "asserts": 1,
          "failures": [],
          "duration_seconds": 0.04
        },
        {
          "name": "Subtracts_TwoPositives",
          "passed": false,
          "asserts": 1,
          "failures": [
            {
              "message": "AssertEquals_INT failed",
              "expected": "1",
              "actual": "2",
              "line": 42
            }
          ],
          "duration_seconds": 0.32
        }
      ]
    }
  ],
  "summary": {
    "suites": 1,
    "tests": 2,
    "asserts": 2,
    "failures": 1,
    "errors": 0,
    "duration_seconds": 0.36
  }
}

expected, actual, and line are extracted from the failure body on a best-effort basis using common TcUnit assertion message patterns. If a particular assertion's body doesn't match, the message field still carries the full failure text verbatim.

Troubleshooting

Symptom Likely cause
Tests did not finish within Ns The suites are slower than TimeoutSeconds; raise via TCKIT_TEST_RUN_TIMEOUT (HTTP side) and re-deploy with a higher -TimeoutSeconds if necessary.
TcUnit results XML not found at <path> Either xUnitEnablePublish wasn't set TRUE on the TcUnit placeholder (publisher off; no XML ever written), or the boot project hasn't activated yet — re-deploy then re-run_tests.
XML not refreshed within 5s of suites finishing TcUnit emitted nothing despite the suites-finished flag flipping; usually means a path permission issue or the file was locked by another reader.
TcXaeMgmt module not found The bridge depends on Beckhoff's TcXaeMgmt PowerShell module for ADS operations. Easiest fix: run tckit doctor and accept the install prompt. Manual: Install-Module -Name TcXaeMgmt -Scope CurrentUser -Force. See bridge setup for offline / TwinCAT-bundled alternatives.