Skip to content

Testing

Unit, integration, end-to-end, security, load and accessibility.

Updated View as Markdown

Tests protect the trading platform from regressions. Follow the test pyramid: put most coverage at the unit layer, integration tests in the middle, and a small, high-value end-to-end layer. Do not turn it into an “ice-cream cone” with slow, fragile end-to-end tests.

The Testing Handbook is NinjaTrader’s main testing guide. This page sets the standard. The handbook explains how to meet it for each stack.

Layers

Layer Expectation
Unit Test pure logic by itself. This is the default place to test.
Integration Test real boundaries with real dependencies in containers. Do not mock the seam you are testing.
End-to-end Test against deployed staging with separate test users for each run. Regression suites live in Zephyr.
Security Run SAST for each pull request, DAST against web and mobile endpoints, and third-party penetration testing every year.
Load Run tests wherever a latency or throughput budget is stated. Threshold failures fail the build.
Accessibility Meet WCAG 2.2 Level AA on every user-facing surface. The author owns this. Automation is required but not enough.

Keep test data isolated and disposable. A bugfix must include a test that fails without the fix. Flaky tests are defects. Quarantine them with a ticket or delete them. Never rerun them until they pass. No automated suite writes to production.

Frameworks per surface

Make “unit” specific. Use the standard framework for each language in the stack.

Surface Unit End-to-end
Scala backend ScalaTest or MUnit
TypeScript / Node Vitest or Jest Playwright
Python pytest
Dart (Flutter) flutter_test widget tests
C# (.NET) xUnit or NUnit
C++ ingest GoogleTest

Property-based testing

Example tests cover the cases you considered. Property-based tests cover input cases you did not consider. Use them for trade math, FIFO matching, FIX round-trips, and settlement boundaries. Use ScalaCheck on the JVM, fast-check for TypeScript, and Hypothesis for Python. The NT-NinjaTrader/testing-handbook ScalaCheck cookbook (pages/stack-cookbooks/backend-services/scala/property-scalacheck.md) is the official in-house example, and tradovate/servers already includes ScalaCheck.

The trading protocol uses more than one wire format. Add golden or snapshot tests and parser fuzzing for FIX serialization round-trips and message parsers.

Integration testing

“Real dependencies in containers” means Testcontainers: start a real Postgres, MySQL, or Kafka instead of mocking the seam under test.

class LedgerSpec extends AnyFlatSpec with Matchers with ForAllTestContainer {
  override val container = PostgreSQLContainer()

  "ledger" should "persist a posting" in {
    val repo = new LedgerRepo(container.jdbcUrl)
    repo.post(entry)
    repo.balance(acct) shouldBe entry.amount
  }
}

Contract testing

End-to-end tests for every service pair are fragile and slow. Use consumer-driven contract tests with Pact at service seams. This lets the Scala, TypeScript, and Node services change independently. Verify contracts against the domain model in tradovate/master-scheme.

Determinism

Flaky tests are defects. Remove their causes:

  • Inject the clock. Do not use System.now or DateTime.now in code under test.
  • Do not use Thread.sleep or arbitrary waits. Wait for a condition, not a set time.
  • Seed all randomness so failures can be reproduced.
  • Fix the timezone and locale.

Coverage and mutation testing

Coverage shows whether lines ran. It does not show whether tests would catch a defect. Gate on diff coverage for changed lines instead of a global percentage, and enforce it through quality gates. Measure suite strength with mutation testing, using PIT for the JVM and Stryker for TypeScript. Run it periodically, not as a per-PR gate.

Load testing

State a latency or throughput budget wherever one exists, and fail the build when it is exceeded. Use Gatling (Scala-native) or k6. Connect load budgets to the SLOs and golden signals in observability.

export const options = {
  thresholds: {
    http_req_duration: ['p(95)<250'], // 95th percentile under 250ms
    http_req_failed:   ['rate<0.01'], // under 1% errors
  },
};
// k6 exits non-zero when a threshold is breached, so CI fails.

Security testing

Run SAST for each pull request, DAST against web and mobile endpoints, and a third-party penetration test every year. The security standard owns these controls and the OWASP baselines behind them.

Accessibility testing

Automate the testable half of WCAG 2.2 with axe-core or Lighthouse for web, and Flutter’s accessibility APIs for mobile. Automation is required but not enough. Also do a manual screen-reader check.

import { axe } from 'vitest-axe';

test('checkout page has no a11y violations', async () => {
  const { container } = render(<Checkout />);
  expect(await axe(container)).toHaveNoViolations();
});
  • Quality gates, how test layers and coverage are enforced as required checks.
  • CI/CD, where suites run, shard, and fail the build on thresholds.
  • Security, the SAST, DAST, and penetration-testing controls.
  • Observability, the SLOs and latency budgets that load tests defend.
Navigation

Type to search…

↑↓ navigate↵ selectEsc close