Learn Jest - Security & Test Reliability
Series/Learn Jest/Episode 13
Episode 13 of 23

Learn Jest - Security & Test Reliability

This episode covers suite security and reliability: preventing tests from leaking sensitive data, isolating environment-specific logic, making tests deterministic and repeatable, and avoiding flaky tests with proper setup and teardown.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

A good test suite doesn't just test code — it must also be secure and reliable. Episode 13 covers two often-overlooked sides: security and test reliability. On the security side, you must prevent tests from leaking sensitive data like API keys or tokens. On the reliability side, the suite must be deterministic and repeatable — the same results on any machine.

These two topics determine whether you can trust the suite as a release safeguard. A suite that leaks data or fails randomly will be ignored by the team and lose its purpose entirely.

Preventing Tests from Leaking Sensitive Data

Don't Write Secrets in Test Code

The first rule: secrets like API keys, tokens, and passwords must never appear in test code or snapshots. Snapshots that show API responses often accidentally store tokens — and that snapshot then gets committed to Git forever.

JSRead secrets from the environment, not hardcoded
test("client memakai token dari env", () => {
  const token = process.env.API_TOKEN;
  expect(token).toBeDefined();
  expect(token).not.toBe("sk-live-xxxxxxxxxxxx");
});

process.env.API_TOKEN forces the secret to be read from the environment, not written directly in the test. The not.toBe("sk-live-...") assertion is an extra safety net so that placeholders resembling real secrets don't slip through.

Sanitizing Logs and Snapshots

If the code under test prints sensitive data, make sure logging is disabled or sanitized during tests. Mock the logger so it doesn't write to the console, and check snapshot contents before committing. A console full of tokens while tests run is a danger sign that must be fixed immediately.

Isolating Environment-Specific Logic

Distinguishing Test and Production Modes

Code that behaves differently per environment must be tested for each mode explicitly. Don't let test behavior leak into production or vice versa:

JSTest behavior per environment
function urlAPI() {
  if (process.env.NODE_ENV === "production") {
    return "https://api.example.com";
  }
  return "http://localhost:8080";
}
 
test("URL berbeda untuk production", () => {
  const lama = process.env.NODE_ENV;
  process.env.NODE_ENV = "production";
  expect(urlAPI()).toBe("https://api.example.com");
  process.env.NODE_ENV = lama;
});

Code that changes process.env inside a test must restore it to its previous value — the line process.env.NODE_ENV = lama guarantees that, so other tests aren't affected.

Keeping Configuration Centralized

As much as possible, isolate environment logic in a single configuration module that's tested once, instead of scattering if (process.env...) across many files. This reduces the risk of tests depending on uncontrolled global state.

Making Tests Deterministic and Repeatable

Avoiding Time and Randomness Dependencies

Tests that rely on the current time or random numbers are hard to reproduce. Use fake timers for time, and inject random values through parameters:

JSTest time-dependent code
jest.useFakeTimers();
jest.setSystemTime(new Date("2026-08-10T00:00:00Z"));
 
function kunciHari() {
  return new Date().toISOString().slice(0, 10);
}
 
test("kunci hari stabil", () => {
  expect(kunciHari()).toBe("2026-08-10");
});

jest.setSystemTime(new Date("2026-08-10T00:00:00Z")) locks the date, so the test produces the same value whenever it runs — on a laptop or in CI.

Avoiding Hidden Execution Order

Never write a test whose result depends on the order in which test files run. Every test must build its own state in beforeEach and dispose of it in afterEach. This is the key to determinism in a suite that runs in parallel.

Avoiding Flaky Tests

Proper Setup and Teardown

A flaky test almost always comes from incomplete setup: un-cleaned files, unfinished timers, or un-reset mocks. A thorough standard pattern:

JSAnti-flaky setup teardown pattern
beforeEach(() => {
  jest.clearAllMocks();
});
 
afterEach(() => {
  jest.useRealTimers();
  nock.cleanAll();
});

jest.useRealTimers() restores real timers after every test — preventing fake timers from leaking into the next test. The combination of clearAllMocks and cleanAll keeps mocks and interceptors clean.

Disciplined Investigation

When a flaky test appears, don't ignore it. Record its pattern, reproduce it with --runInBand, and fix the root cause. Flaky tests left alone will pile up and eventually erode the team's trust in the entire suite.

Wrap Up

Episode 13 covered suite security and reliability: keeping secrets out of test code and snapshots, isolating per-environment logic, building deterministic and repeatable tests, and avoiding flaky tests with disciplined setup and teardown.

Key takeaways:

  • Secrets must never appear in test code or snapshots — read them from the environment.
  • Test per-environment behavior explicitly and restore the env afterward.
  • Fake timers make time-dependent tests deterministic.
  • Every test builds its own state; don't rely on order.
  • Clean up mocks, timers, and interceptors in afterEach.
  • Investigate flaky tests thoroughly; don't let them pile up.

In the next episode, episode 14, we'll cover running Jest in CI/CD — integration with GitHub Actions, GitLab CI, and Jenkins, parallel execution and test splitting, fail fast with selective test runs, and reporting results and coverage badges.

Learn Jest - Security & Test Reliability | Learn Jest