Senior automation techniques
You are no longer just writing tests — you are designing the system those tests live in. Frameworks, pipelines, contracts, data, and people. Your decisions echo across the whole team.
1. Framework architecture
A senior engineer designs the framework so other people succeed in it. Separate the layers and the boundaries become obvious:
- Tests — describe scenarios in business language. No drivers, no HTTP clients, no selectors.
- Actions / page objects / API clients — express the system under test.
- Core / utilities — config, waits, reporting, fixtures, logging.
- Drivers — browser, API, DB, message bus. Swappable behind interfaces.
Design for the second test author, not yourself. If a newcomer needs 30 minutes and a README to add a test, your framework is working. If they ask you "where does this go?", it isn’t.
Smell: Every test file imports WebDriver directly and contains 3–4 findElement calls.
What have you given up?
2. CI/CD integration
The pipeline is where tests create (or destroy) trust. Senior responsibilities:
- Stage tests by speed and stability — smoke on every PR (minutes), full regression nightly or pre-release, long-running perf/accessibility in dedicated lanes.
- Fail fast — a suite that takes 90 minutes to tell you your build is broken is a support ticket, not a signal.
- Artifacts — screenshots, videos, HAR files, logs, JUnit XML — every failure should be triageable without re-running.
- Quarantine lane — flaky tests run separately; they don’t block merges but they do get fixed.
- Environment parity — match CI and local as closely as possible (Docker, containerised browsers like Selenium Grid or Playwright’s built-in).
Tool-adjacent references: GitHub Actions, Docker.
3. Parallelisation & sharding
A one-hour suite becomes a six-minute suite with ten workers — if the suite is isolated. If it isn’t, parallelisation just makes the flakes louder. Prerequisites:
- Tests create their own data with unique identifiers (UUIDs, timestamps + worker ID).
- No shared writable state — no shared users, no shared DB rows, no shared files.
- Per-worker browser/session (or parallel-safe fixtures).
- Sharding strategy that balances runtime, not test count (long tests get their own shard).
4. Contract testing
End-to-end tests break whenever any service changes. Contract tests break only when the agreement between services changes — which is exactly what you want.
- Consumer-driven (Pact) — the consumer declares what it expects; the provider verifies it still meets the expectation.
- Schema-based (OpenAPI/JSON Schema) — shared spec, both sides validated against it.
- When it pays off — microservice architectures where flaky end-to-end is draining the team.
Contract tests don’t replace end-to-end — they replace most of it, so the end-to-end suite can shrink to critical journeys.
5. Performance automation
Not full load engineering, but enough to catch regressions in CI. At senior level you should be comfortable setting up:
- A k6 or JMeter smoke test that fails the build on obvious regressions (p95 latency, error rate).
- Baseline thresholds per endpoint, version-controlled alongside the code.
- A dedicated, production-like environment — perf tests against a developer laptop are worse than useless.
- Clear separation of load (many users), stress (find the breaking point), soak (hours of steady load, memory leaks).
Result: Mean response time is 120 ms. Your threshold passes.
Why is "mean" the wrong metric?
6. Test data management
Test data is a first-class concern once you run in parallel across environments. Approaches, from cheapest to most robust:
- Test-created data — each test creates and (optionally) tears down. Most isolated; costs setup time.
- Fixtures / factories — named builders that produce valid entities with sensible defaults (
UserFactory.premium().build()). - Seeded baselines — a known dataset loaded pre-suite; tests read but don’t mutate. Fragile as the app evolves.
- Synthetic generation — Faker + domain rules. Great for breadth, hard to debug when a random value triggers a failure.
Never use production data in non-prod environments without deterministic anonymisation. It’s a compliance and privacy landmine.
7. Observability & telemetry
In 2026, a test failure shouldn't just show a stack trace. It should correlate with the entire system state. Distributed tracing (OpenTelemetry) allows you to see exactly which microservice failed during your end-to-end run:
- Trace Correlation: Pass a unique
X-Test-IDin your automation headers to correlate test logs with backend traces in Jaeger or Honeycomb. - Log Aggregation: Centralise logs from the test runner, the application under test, and the database into a single timeline.
- Dashboards: Track the "Reliability Score" of your suites — distinguish between infrastructure failures, environment flakes, and real application bugs.
- Actionable reporting: Self-documenting failures that include the exact API response, state of the DB, and a video of the UI at the moment of impact.
8. Agentic AI in QA
The role of the senior engineer is shifting from writing code to managing AI agents that write and maintain code. In 2026, automation is augmented by:
- Self-Healing Locators: Using LLM-based agents to identify elements when the DOM structure changes, reducing maintenance by up to 80%.
- Autonomous Data Gen: Agents that understand your database schema and generate logically consistent, complex datasets for every test run.
- Risk-Based Selection: AI that reviews your code changes and automatically selects the minimum viable set of tests to run, saving pipeline time.
- Synthetic User Agents: Using AI to "explore" the app and find edge cases that were never scripted in a formal test plan.
9. Standards & mentoring
Your technical leadership shows up in the things the team doesn’t have to think about:
- Conventions: test naming, file layout, tag strategy, commit style.
- Review rubric for test PRs — what earns a block, what earns a nit.
- An onboarding path for juniors (junior techniques) and mid-levels (mid-level techniques).
- Runbooks: how to triage a red pipeline, how to add a new browser, how to investigate a flake.
Seniors write down the things that would otherwise live in someone’s head. That’s the leverage.
Reference mapping
| Area | Reference |
|---|---|
| Test strategy & planning | Test planning, Risk-based testing |
| Non-functional coverage | Accessibility, Security |
| Metrics & quality signals | Test metrics |
| Branch/condition/path coverage mindset | Branch coverage, Condition coverage, Path coverage |
| CI/CD & infra | GitHub Actions, Docker |
| Performance | k6 |
| ISTQB alignment | CT-TAE (advanced), CTAL-TTA (Technical Test Analyst) |
10 Now You Try
Three graded exercises — spot, fix, then build. These target senior calls: framework design, CI gating, parallelisation, and flaky-test triage. Write your answer, run it for AI feedback, then compare to the model answer.
A TransitNZ vehicle-licensing platform runs this pipeline policy. Critique it as a senior: what makes the build slow to give a signal, what makes it untrustworthy, and what you would change.
Show model answer
What makes feedback slow: - The whole 1,400-test suite (incl. perf) runs on every PR. An 85-minute gate is a support ticket, not a signal. Stage it: a fast smoke set (a few minutes) on every PR; full regression nightly/pre-release; perf and accessibility in dedicated lanes. - The suite isn't parallelised. Once isolated, shard across workers (balance by runtime, not test count) to cut wall-clock time. What makes it untrustworthy: - Re-running known-flaky tests up to 5× and counting any pass as green masks real intermittent bugs and trains the team to ignore red. Move flaky tests to a quarantine lane that doesn't block merges but must be fixed within a sprint — don't paper over them with retries. - No artifacts means every failure costs a local re-run. Save screenshots, video, HAR, logs and JUnit XML so any failure is triageable without re-running. What I would change: - Stage by speed/stability, parallelise an isolated suite, quarantine (don't retry-to-green) flakes, and make every failure self-documenting via artifacts. Define explicit, versioned gates: which signals block vs warn.
A KiwiFirst Bank regression suite was sharded across 10 workers and immediately started failing intermittently. The tests below share state. Rewrite the approach so the suite is parallel-safe, and name each isolation rule you applied.
Show model answer
Parallel-safe redesign:
- Per-test (or per-worker) user and account, created via the API with unique identifiers (UUID or timestamp + worker ID), e.g. qa+{uuid}@bank.test and a freshly created account. No shared "qa@bank.test", no shared #1001.
- Each test seeds the exact starting balance it needs and asserts only on its own account — no cross-test balance dependence.
- Remove the order assumption: each test stands alone and passes when run first, last, or shuffled. Enforce it by running with --shuffle / --random in CI.
- Drop the shared results.json. Let the framework's reporter aggregate per-test results; if files are needed, write to a per-worker path.
Isolation rules applied:
1. Unique data per test (no shared user/account).
2. No shared writable state (no shared rows, no shared file).
3. Each test creates its own preconditions via the API and (ideally) tears them down.
4. Order-independence, verified by randomised execution.
Shard by runtime, not test count, so long tests get their own shard.
An Revenue NZ myIR end-to-end suite has a flake rate climbing past 6%. Write a short triage runbook a mid-level could follow: how to detect and quantify flakes, how to classify a single failure (infra vs environment flake vs real bug), the immediate containment action, and the policy for getting a quarantined test back to green. Use concrete signals, not vibes.
Show model answer
1. Detect & quantify: - Track pass rate per test over time; flag any test whose failures aren't reproducible on re-run. Report a suite-level flake rate (% of pipeline failures caused by tests, not code) and a top-offenders list. Use trace correlation (an X-Test-ID header) so test logs line up with backend traces. 2. Classify a single failure (use artifacts, don't guess): - Infra failure: runner died, network/DNS, image pull, timeout before the app responded → not a code or test fault. - Environment flake: app/data not ready, dependency slow, shared-state collision → test/timing or isolation issue. - Real bug: reproducible, app behaved incorrectly with correct inputs → log a defect, keep the test red. Decide using saved screenshots, video, HAR, logs, the exact API response and DB state — no local re-running required. 3. Immediate containment: - Move a confirmed flake to the quarantine lane so it doesn't block merges, with a ticket and an owner. Never retry-to-green in the main gate; never delete the assertion or add a sleep. 4. Back-to-green policy: - Quarantined tests are fixed within a defined window (e.g. one sprint), root cause addressed (sync not sleep, deterministic data, isolation), then must pass N consecutive shuffled runs before returning to the blocking suite. Track the quarantine count as a framework-health metric.
Self-Check
Click each question to reveal the answer.
Q1: What is the test you should design a framework to pass — the human one, not the automated one?
A newcomer should be able to add a test with about 30 minutes and a README, without asking "where does this go?". You design for the second author, not yourself: tests in business language, the driver hidden behind actions/page objects, and clear conventions for where each kind of code lives.
Q2: Why is staging tests by speed and stability more valuable than running everything on every PR?
A suite that takes 85–90 minutes to report a broken build is a support ticket, not a signal. Run a fast smoke set on every PR (minutes), full regression nightly or pre-release, and long perf/accessibility runs in dedicated lanes — so developers get fast, trustworthy feedback where they need it.
Q3: What must be true before parallelisation actually speeds a suite up rather than amplifying flakes?
The suite must be isolated: each test creates its own data with unique identifiers, there is no shared writable state (no shared users, rows, or files), and each worker has its own browser/session. Shard by runtime, not test count, so a long test gets its own shard. Parallelising a non-isolated suite just makes the flakes louder.
Q4: Why are consumer-driven contract tests preferable to a large end-to-end suite in a microservice estate?
End-to-end tests break whenever any service changes; contract tests break only when the agreement between services changes — which is what you actually care about. They don’t replace end-to-end entirely, but they replace most of it, letting the end-to-end suite shrink to a few critical journeys.
Q5: Why is "mean response time passed the threshold" a misleading performance signal?
The mean hides the tail. Your p95/p99 could be several seconds while the average looks fine, and users experience the tail, not the average. Assert on percentiles (p95/p99) and error rate, with version-controlled per-endpoint baselines, against a production-like environment.