Don't exhaust yourself with manual testing: All the techniques for E2E automation and CI/CD pipeline building with Playwright
How many more hours are you going to throw away "splashing screens before release"?
Just before pressing the deploy button, I check the login screen with trembling hands, add items to my cart, and check the payment flow. As you do this over and over again, the value of your code becomes obsolete and your precious brain resources are wasted.
I used to be like that too. I fall into a deadly loop of "I don't have time to write test code" and end up injecting bugs in late-night deployments, making me look pale. However, after introducing Playwright, the landscape changed. This time, we will share practical tips for breaking away from the tedious manual testing and creating an environment where quality is guaranteed while you sleep with CI/CD.
1. Ditch the “thinking” of authentication and reuse state.
The biggest mistake many engineers make is starting every test case with "Login." This increases test execution time linearly, and as a result no one runs the tests.
The golden rule is to use `storageState` to save and load browser cookies and LocalStorage.
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
// Reuse authenticated state
storageState: 'playwright/.auth/user.json',
},
});
`
⚠️ Security alert
The JSON stored in `storageState` includes session information and tokens. Never put it under Git control. It is essential to add `.auth/` to `.gitignore`, and in a CI environment, run the login test only once and pass it as an artifact. Regarding token expiration (refresh strategy), it is recommended to implement a login check using `globalSetup` at the start of the test.2. Abandon “waiting” code and write it declaratively
The moment you write `page.waitForTimeout(3000)`, the test code becomes "garbage". Don't try to solve network fluctuations or rendering delays with "prayers."
Trust Playwright's powerful auto weight.
// Bad example: "Prayer" to wait 3 seconds
await page.waitForTimeout(3000);
await page.click('.submit-btn');
// Good example: "Declaration" that waits until the state changes
const button = page.locator('.submit-btn');
await expect(button).toBeVisible();
await button.click();
`
From a computer science perspective, it is extremely illogical for humans to think, ``We just have to wait this long.'' Triggers should be when an element appears, disappears, or the network is idle.
3. “Trap” in CI environment: Fill in environment differences with code
It works locally but crashes on GitHub Actions. The true cause of this phenomenon is, in most cases, an ``environmental mismatch.'' Time zone, screen size, and font rendering. If you leave these things unattended, your CI is just running a "gacha" instead of a test.
`
# .github/workflows/playwright.yml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Playwright
run: npx playwright test
env:
TZ: Asia/Tokyo # Fixed time zone
CI: true
- name: Upload Report
if: always() # Save report even if it fails
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 7
`
Don't throw away traces on failure. `playwright-report` records DOM snapshots and videos at the moment of failure. With this, you won't have to worry about why something crashed while debugging late at night.
4. “Next Step” for Growth
Test automation is not just about improving work efficiency. This is an investment to acquire the "right to boldly refactor without fear of bugs."
If you don't have any tests for your current code, start with just one case: ``Login and transition to My Page.'' There's no need to aim for perfection. All we need is one more automated part than yesterday.
Learning Resources
- Playwright official documentation - Please read this carefully first.
- Testing Library - Learn the philosophy of writing tests from a "user's perspective".
Stop wasting your time with manual testing starting tomorrow. Sleep with the code.