There Are No Automated Tests, or No CI Pipeline Actually Runs Them
Is it a problem if I have test files but no CI workflow running them? Yes: test files that nothing runs automatically provide zero protection, they only feel safe. Start with one smoke test on the riskiest path, money handling or auth, add a minimal CI workflow that runs on every push, and make deploy conditional on it passing. A slow or interactive suite is worse than a small fast one that actually runs.
Seen in 26 of 450 scanned projects (6%). Counts projects with either zero automated tests on critical code paths, or test files present with no CI workflow actually executing them on the default branch.
How to tell you have it
- Dozens of test files exist in the repo but there's no CI config, or the CI config only exists on an unmerged branch
- Money-handling or authentication code has zero test coverage of any kind
- A CI workflow runs tests but the deploy step doesn't check whether they passed
- The lint or test command is interactive, so it hangs forever when run headless in CI
- The last commit to touch any test file predates the last several feature merges
Why it matters
A test file that no automated system runs is documentation at best. It records what someone believed the code should do at one point, but without CI enforcing it on every change, nothing stops a later commit from silently breaking that behavior. The team gets the psychological comfort of 'we have tests' without any of the actual protection.
Money-handling and auth code are the highest-consequence places to have zero coverage, because their failure modes are the ones that cost real money or leak real data, and they're also the code most likely to be touched by an urgent, under-reviewed hotfix. A single smoke test on the critical path catches the most common class of regression: something that used to work now throws.
CI that runs tests but isn't wired into the deploy decision is a specific and common half-measure. The workflow goes green or red in a tab nobody's watching while the deploy script runs unconditionally on every push. The fix isn't more tests, it's one line in the deploy pipeline that checks the previous job's exit code.
An interactive lint or test command, one that prompts for input or expects a TTY, works fine on a developer's machine and hangs indefinitely in a non-interactive CI runner. This kind of failure is invisible locally and only shows up as a timed-out CI job, which is often mistaken for flakiness rather than diagnosed as a fundamentally broken headless invocation.
How Heygents detects it
Deep Scan reads the repository for test files and their corresponding CI workflow config (GitHub Actions, GitLab CI, or equivalent), checks whether that config exists on the default branch versus only a feature branch, and inspects whether the deploy step or script is gated on the test job's result. It also attempts to identify whether the configured test or lint command requires a TTY.
How to fix it
- Write one smoke test on the riskiest path first Don't aim for coverage percentage. Pick the single function or route where a silent regression would cost the most, usually payment or auth, and write one test that exercises the happy path end to end.
- Add a minimal CI workflow A single job that installs dependencies and runs the test command on every push and pull request is enough to start. Resist the urge to build a large matrix before there's even one gate in place.
- Make the lint and test commands headless-safe Check the exact command CI runs against a non-interactive shell locally before trusting it in CI. Any command that expects stdin will hang the job until it times out.
- Gate deploy on the test job Make the deploy job depend on the test job explicitly, so a red test run blocks the deploy instead of running in parallel and being ignored.
- Merge CI onto the default branch A CI workflow that only exists on an unmerged feature branch protects nothing. Merge it to the branch that deploy actually reads from, and confirm with a real push that the workflow triggers.
Write one smoke test on the riskiest path first
test('checkout charges the correct amount', async () => {
const result = await charge({ amount: 1999, token: 'tok_test' });
expect(result.status).toBe('succeeded');
expect(result.amount).toBe(1999);
});
Add a minimal CI workflow
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci
- run: npm test
Make the lint and test commands headless-safe
CI=true npm run lint < /dev/null
CI=true npm test < /dev/null
Gate deploy on the test job
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh
Merge CI onto the default branch
git checkout main
git merge ci-setup-branch --no-ff
git push origin main
Find this in your own projects, automatically
Heygents runs a read-only Deep Scan across every project you own, finds issues like this one, and hands you a ready-to-run fix an AI agent can execute and verify. A solo developer gets the audit, the backlog and the fix loop in one place.
Open Heygents →Frequently asked questions
Should I aim for full test coverage before adding CI?
No, do it in the opposite order. A CI workflow running one meaningful test today is more valuable than a comprehensive suite planned for someday, because it establishes the habit and the gate immediately, and every test added afterward compounds on top of an already-running pipeline.
What counts as the riskiest path to test first?
Anything where a silent regression costs money or exposes data: payment processing, authentication and authorization checks, and any endpoint that writes to the database without validation. If none of those exist, start with whatever a bug report would be most embarrassing to explain.
How do I keep the suite fast as it grows?
Separate fast unit tests from slower integration or end-to-end tests into different CI jobs, and run the fast ones first so a broken build fails quickly. Avoid spinning up a full database or browser for tests that don't need one.
Is a CI badge in the README enough proof it's working?
No, a badge can go stale or point at a workflow file that was later deleted or disabled. Confirm by checking the Actions or Pipelines tab directly for a recent run against the default branch, and check the deploy script separately to confirm it actually depends on that run's result.