The Testing Academy · Class Notes Friday, 28 August (IST)
Live class · study guide

Adding one feature with AI, without becoming a blind AI user

The feature is small: read credentials and checkout data from a .env file. The method is the lesson. Plan mode before any code, then push back on the plan twice, then ask the one question that finds the CI break nobody saw, then approve the changes file by file and prove the injection actually worked. The formula at the end is the part that survives this framework.

By Pramod Dutta, The Testing Academy. Study notes from the live Playwright 2x class, rebuilt from the session recording, the batch repository and the Eraser deck. The shipped code was read from the repository rather than transcribed, and it differs from the walkthrough in two places that are called out on this page: the util landed at a different path than the one named on screen, and the reason it has to be a module is a hoisting detail the session did not state.

01

Where this sits

The previous class finished the fixtures and got two end-to-end specs running. The framework is now roughly 80 to 90 percent built, and that changes what comes next:

Now it is time to start using GitHub Copilot or Claude Code, because the base framework is already ready. On top of it, extra things can be added very easily.

Today adds one small feature, reading credentials and checkout data from a .env file, and spends most of the hour on how it gets added rather than what it is.

The reason is blunt, and it came from the other side of the interview table:

I am also taking interviews, and this is the problem I have very commonly found. If I ask how you added the environment variable, they say "sir, I just used a command in GitHub Copilot, add the env support". That is not the answer we wanted.

02

The formula

The whole method fits in nine steps, and the session ends by writing them into the repository's learnings folder.

01Understand the repo 02Ask it questions 03Plan mode 04Review the plan 05Blast radius 06Implement 07Trim the comments 08Modularise 09Verify, do not trust Steps 3 to 5 are wherethe real work happens. Most people skip them.
Nothing here is specific to Claude Code. The same nine steps apply to GitHub Copilot, Codex, Amazon Q or any other agent.
03

Plan mode, not build mode

The first prompt does not ask for code. It asks for a written plan, and it names what the plan must contain:

Create a temporary plan for it, where you will mention the files touched and the files changed, and give me all the details of what changes you are going to make.

The agent came back with questions first, which is the behaviour you want. The plan it then produced was genuinely useful:

  • dotenv was already installed. It was in devDependencies and already imported by playwright.config.ts. Nothing to add.
  • Two alternatives were considered and rejected, both encryption-capable wrappers around dotenv. Not needed here.
  • It listed the files it would touch and, importantly, the files it would leave untouched.

Plan mode is not a Claude Code feature. Amazon Q has one too. The point is not the tool, it is refusing to let anything be written until you have read what it intends to write.

04

Reading the plan critically, twice

Here is where most people stop. The plan looked fine. It was approved by nobody.

Push-back one, on structure. The plan put the env-reading helper inline in the spec file:

Can we create some util which can make it more modular in nature, and optimise it with minimum changes?

The reply was "good call", and the helpers moved out into their own module. That is the right instinct generalised: a helper written inside the file that uses it is a helper you will write again tomorrow.

Push-back two, on damage. The second question is the one that earns its keep:

Can you please check if other spec files do not break due to these changes? And make sure we add the files without too many comments.

That found a real problem the first two passes never mentioned.

05

The blast radius, and the CI break

.env is in .gitignore. That is correct and it is also the bug.

GitHub Actions checks out a repository with no .env file. A fail-fast check that runs when the module loads therefore throws at collection time, before any test runs, which fails the entire suite rather than the one spec that needed the file.

The repository's own note records how that was confirmed, and it is worth copying as a technique:

Terminal
mv .env .env.bak && npx playwright test

All five tests died. The fix is a CI step that seeds the file from the committed example:

Terminal
cp .env.example .env

which also makes .env.example load-bearing rather than decorative.

Asked in the room, and worth thinking about before reading on: if a check throws while the module is being imported rather than inside a test body, how many tests fail? All of them in the run, because the failure happens during collection. Moving the check inside the test body would scope the failure to one test, but it also delays the signal. The trade taken here was to keep the fail-fast check and seed the file in CI.

06

What actually shipped

The util landed at src/config/env.ts and exports three readers:

TypeScript
/** Reads `.env` values. Importing this module loads `.env` into process.env once. */
import dotenv from 'dotenv';

dotenv.config({ quiet: true });

export function requireEnv(key: string): string { ... }   // throws when unset
export function envOr(key: string, fallback: string): string { ... }
export function assertEnv(...keys: string[]): void { ... } // reports all missing keys at once

Two differences between the session and the repository. The util was named environment.ts on screen and shipped as src/config/env.ts. And the deeper reason it has to be a module was never stated in class, only its benefit. It is worth knowing, because it is a genuine trap.

WRITTEN MID IMPORT LIST, BREAKS import { test } from '@playwright/test' dotenv.config() import { credentials } from './credentials' Babel hoists both imports above the config call, so credentials.ts reads process.env before .env is loaded. LOADED BY A MODULE, WORKS // src/config/env.ts dotenv.config({ quiet: true }) export function requireEnv(...) Hoisting now works for you: the module body runs before any importer's body, every time.
Playwright transpiles TypeScript through Babel, whose CommonJS transform hoists every require to the top of the file.
07

Fail fast, or fall back, per value

The shipped design does not pick one strategy for everything. It picks per value, which is the detail worth stealing.

Value Reader Why
Checkout item id requireEnv There is no sensible default. A wrong product silently tests nothing
Login user and secret envOr The practice app has known defaults, so a missing file is not fatal
Checkout first name, last name, postal code envOr Falls back to generated data, so the spec still runs

In the spec, that reads as:

TypeScript
assertEnv('STANDARD_USER', 'TTA_SECRET');
const ITEM_ID = requireEnv('CHECKOUT_ITEM_ID');

and in the generator:

TypeScript
firstName: envOr('CHECKOUT_FIRST_NAME', generated.firstName),

dotenv is left at its default override: false, which means a real shell or CI variable beats the file. That is the behaviour you want: the file is for your laptop, the environment is for the pipeline.

08

Approving, and then proving

Approval was manual, file by file, not auto-accept. Each diff was read aloud before being accepted, and the running commentary was the point:

Now, if you can see, I am not a blind AI user. First the library, then the env module was created, then the data generator changed, then the credentials file.

After the changes, the existing specs were re-run as a regression, including the parallel ones, and all passed.

Then one more step, which the repository records and the session did not spell out. A spec that reads .env and a spec that ignores it look identical when the file happens to contain the same values as the defaults. So the injection was proved by overriding it from the shell and watching the run change:

Terminal
CHECKOUT_FIRST_NAME=EnvProof npx playwright test

That is the difference between believing a feature works and knowing it does.

09

Explaining it afterwards

Two things get produced at the end, and neither is framework code.

An explainer. A skill called EL5, explain it like I am five, generates a temporary HTML page describing what was added and why, plus a hand-drawn-style diagram. It goes in a folder that is not committed with the framework, and its audience is the person reviewing your pull request:

The person reviewing your PR also does not understand what you have done. They will also try to use AI. Rather than that, show them this.

A learnings note. The formula and the findings go into a learnings folder, kept deliberately separate from docs. Learnings are for you; docs are for the framework.

The reason both exist is the same reason the whole class exists: if you cannot explain how a feature was added, you have not added it. That is the sentence to remember from this session.

10

Skills into the repository

The last few minutes add skill files from the Playwright skill pack into the .claude and GitHub Copilot directories, so the agent has repeatable procedures rather than fresh guesses.

Two rules were attached, and both are judgement calls the AI could not make for you:

  • Only the skills that belong to this framework. A Jira requirement analyser and a test plan generator are useful, and they are not part of a Playwright automation framework. Keep those separate.
  • Modify every skill before you keep it. The pack ships generic skills. A page object creator that does not know your page object conventions will produce something you then have to rewrite.
11

Tasks and announcements

Before the next class

  • Watch the Web Fundamentals for API testers video. This is a prerequisite, not a suggestion: the next class starts API testing, Postman, and automating RESTful Booker. The video is being shared, and emailed if possible.
  • Reach framework parity within 7 to 10 days: the end-to-end tests, the skills, and every file. The reasoning given is practical, that you cannot run something in Docker later if you never wrote the end-to-end test now.
  • Post your Claude Code 101 certification on LinkedIn, ideally around 9 in the morning when the most people see it.

Coming later

  • API testing, starting next class: fundamentals, Postman, RESTful Booker, and API fixtures.
  • A flaky test analyser agent, which finds the root cause rather than asking you to maintain around it.
  • Sharding and workers, in a session of their own. Docker was already covered in the SDET master class.
  • An AWS certification roadmap was requested; the answer is coming on the SDET club thread after some research.