Frontend engineer · Aepsy · Now · React, TypeScript, Ionic/Capacitor, Apollo GraphQL

Tests that block the merge

Aepsy is a Swiss digital mental health platform: one TypeScript codebase shipping as the web app, the iOS app, and the Android app. The testing effort there is the team's; my part was making its standard enforceable. I wrote the commit that turned scattered practice into lint rules, machine readable test writing skills, and one fixed test environment, and I made the test suite a required check that blocks a merge.

SHAPES HOW TESTS GET WRITTENDOCUMENTSSKILLSSTATIC RULESBLOCKS THE MERGEMERGE GATE
Three layers shape how tests get written. One blocks the merge.

web, iOS, and Android from one codebasetests required to mergeVitest, JUnit, pytest

Problem

Aepsy is a digital mental health platform, and one TypeScript codebase ships as its web app, its iOS app, and its Android app. A change does not stay in one place: the same code reaches all three, and two of the three move at app store cadence rather than at the speed a fix can be written.

The testing effort there is a team effort, which is exactly where a convention goes quiet. Written down, a convention is something people mean to follow. My part of the work was making the standard something the repository holds rather than something a reviewer has to remember under a deadline.

Constraints

Three platforms come out of one codebase. The web app deploys continuously and the native apps move at app store cadence, so anything that has to land everywhere at once has to be planned around the slower channel.

Tests have to be deterministic. The test environment is committed and the timezone is forced to UTC, so a date behaves the same on every machine that runs the suite.

The standard has to survive tests written with AI tooling. A convention that lives only in prose does not survive that, so whatever holds it has to be readable by the tooling itself.

There is no automated end to end tier, so the tiers below it carry the argument that a change is safe. The client strategy is integration first for the same reason: many integration tests, few unit tests.

What I did

The testing effort at Aepsy is collective. Others built the runner foundation and wrote most of the tests; what follows is the part that is mine.

I wrote the standardization commit. It turned scattered practice into things a machine reads: lint rules on test files, the first version of the test writing skill, fixtures consolidated onto one shape, test configuration split out on its own, and the test environment fixed. Separately I brought new test tiers into CI.

The suite went from a few dozen files to hundreds in the months after the standard landed, written by the whole team, most of it not by me, against one set of templates, fixtures, and banned patterns. Enforcement arrived in steps; the last step is the one the title names: the suite is now a required check, and a merge waits for it.

The other thread was two factor authentication across all three platforms at once. I built the foundation: the client service, a native plugin with an iOS and an Android implementation behind one TypeScript interface, native for security reasons, and the enrollment and challenge UI, all merged dark behind a single shared flag. Weeks of polish by the team followed, and production came on with a one line change. The real coordination problem is the release channels: the web app deploys continuously, the native apps move at app store cadence, so the native releases had to be ready before the flag ever moved. Accounts with and without two factor coexist indefinitely; that is a design property, not a transition.

WEBIOSANDROIDMERGED, FLAG OFFONE LINE, FLAG ON
Three platforms from one codebase. The native releases had to be ready before the flag moved.

Decisions

DecisionWhyWhat I rejectedWhat it cost
Enforce in the merge path, not in reviewA rule that lives in review discipline disappears under deadline; a required check does notRelying on the review rubric aloneA gate proves the tests ran and passed. It cannot prove they assert anything; that still takes review
Machine readable skills for AI written testsTests are increasingly written with AI tooling, and prose conventions do not survive that; templates and banned patterns doA style guide nobody's tooling readsA skill shapes what gets written; it cannot block a hand written commit
One fixed test environmentA committed env and a fixed timezone make time deterministic, the recurring source of flakesPer test setup

Details that mattered

There is no automated end to end layer yet; the strategy documents call it phase two, and scripted manual QA covers the native flows meanwhile.

Three runners have to agree on what a standard means: the client tests run on Vitest, the backend tier on JUnit, and a Python tier on pytest. That is the spread the standard has to hold together, and it is why the rules that matter are the ones a machine reads rather than the ones a document states.