Wave-4colors_1830x900
  • Home
  • concircle blog
  • Running SAPUI5 Unit Tests Headlessly in SAP Business Application Studio
Herbert Kaintz 02/16/2026
2 Minutes

Automated testing before deployment is not optional — it is fundamental.

If tests fail, the build must fail. Simple rule.


In local environments such as Visual Studio Code, implementing this rule is straightforward. Tools like wdi5 or the UI5 Test Runner integrate seamlessly with real browsers and allow unit and integration tests to run directly from the CLI as part of the pipeline.

Things become more interesting inside SAP Business Application Studio (BAS).

 

The Challenge

The goal is clear:

SAPUI5 unit and integration tests should run automatically before deployment.

SAP Business Application Studio runs in a containerized setup. By default, no headless browser runtime (such as Chromium) is available to the CLI.

 

That means there is no browser that can easily be controlled from the command line.

Traditional headless approaches become impractical.

However, BAS does provide:

  • Node.js
  • The UI5 Development Server

Instead of fighting the environment, this solution builds on those strengths.

 

The Approach

The idea:

  1. Run QUnit tests via ui5 serve
  2. Let QUnit report results back to Node.js
  3. Exit the process with:
    • 0 if tests pass
    • 1 if tests fail
  4. Abort the build automatically

The key: Custom UI5 server middleware

 

Custom Middleware to Capture QUnit Results

A small Node.js middleware listens for a POST request once QUnit finishes.

qunitDone.js
module.exports = ({ log }) => { 

  return (req, res, next) => { 

    if (req.method === "POST" && req.url === "/__qunit_done__") { 

      let body = ""; 

      req.on("data", (chunk) => { 
        body += chunk.toString(); 
      }); 

      req.on("end", () => { 
        try { 
          const details = JSON.parse(body); 

          log.info(
            `QUnit finished: ${details.passed}/${details.total} passed, failed=${details.failed}`
          );

          if (details.failed > 0) {
            log.info("Aborting deployment because of failed tests.");
          }

          res.statusCode = 200;
          res.end("OK");

          setTimeout(() => process.exit(details.failed === 0 ? 0 : 1), 50);

        } catch (e) {

          log.error("Failed to parse QUnit payload");
          res.statusCode = 400;
          res.end("Bad Request");
          setTimeout(() => process.exit(1), 50);

        }
      });

      return;
    }

    next();
  };
};
  
 

What This Does

  • Listens for POST /__qunit_done__
  • Receives QUnit summary as JSON
  • Logs results to terminal
  • Exits process:
    • exit(0) → success
    • exit(1) → failure

This exit code allows npm and CI pipelines to react correctly.

 

Sending Test Results from QUnit

QUnit is hooked using QUnit.done.

unitTests.qunit.ts
function isBackground(): string | null {
  return new URLSearchParams(window.location.search).get("background");
}

QUnit.done((details) => {
  void fetch(`/__qunit_done__`, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify(details)
  }).then((res) => {
    if (res.ok && isBackground()) {
      window.close();
    }
  });
});
  

What This Does

  • Hooks into QUnit.done
  • Sends results to middleware
  • Optionally closes browser in background mode

The background parameter differentiates:

  • Interactive runs
  • Automated CI-style runs

 

Register Middleware in UI5

ui5-test.yaml
specVersion: "4.0"
kind: extension
type: server-middleware
metadata:
  name: qunit-done
middleware:
  path: ./script/qunitDone.js
  

 

npm Script Orchestration

package.json
{
  "scripts": {
    "unit-test:background": "echo 'Running unit tests ...' && ui5 serve --port 8081 -o test/unit/unitTests.qunit.html?background=true --config ui5-test.yaml",
    "build": "npm run lint && npm run unit-test:background && ui5 build --clean-dest",
    "deploy": "npm run build && fiori deploy --config ui5-deploy.yaml && rimraf archive.zip"
  }
}
  

Result

  • Tests run before every build
  • A single failing test stops deployment
  • No additional browser tooling required
  • Works reliably inside SAP Business Application Studio

Running npm run deploy:

 

Final Thoughts

This approach isn’t meant to replace full end-to-end testing frameworks.

But for fast, reliable unit test enforcement before deployment, it works surprisingly well — especially in constrained environments like BAS.

 

Sometimes the best solution isn’t adding more tools, but letting existing tools talk to each other in smarter ways.

If you’re working with SAPUI5, QUnit, and BAS — give it a try.

 

Let’s Connect

If you want to discuss this approach, alternative testing strategies, or SAPUI5 quality gates in general, feel free to reach out to me on my LinkedIn — happy to exchange ideas and experiences.

 

At concircle, we help teams build sustainable SAP UI architectures with a strong focus on quality, automation, and long-term maintainability.

If you’re looking to strengthen your SAPUI5 delivery pipelines or introduce meaningful testing strategies in your projects, let’s talk about your specific scenario.

Would you like to learn more about above mentioned solutions?