E2E w Dockerze i efemerycznych środowiskach
Odtwarzalne środowiska testowe to fundament niezawodnego CI/CD. Naucz się używać Docker Compose do lokalnego i CI testowania E2E oraz uruchamiać testy smoke przeciwko efemerycznym deploymentom.
Ten post to część 3 serii Jakość w CI/CD. Część 2 omawiała GitHub Actions — workflow, cache’owanie i integrację Playwrighta.
Wprowadzenie — problem “działa na moim komputerze”
Test suite, który przechodzi lokalnie, ale failuje w CI, jest gorszy niż brak test suite. Szkoli developerów do ignorowania niepowodzeń CI.
Pierwotna przyczyna to niespójność środowiskowa. Twoja lokalna maszyna ma inną:
- Wersję Node
- Wersję bazy danych
- Zmienne środowiskowe
- Opóźnienie sieciowe
- Zachowanie systemu plików
Docker rozwiązuje to, pakując aplikację i jej zależności w kontener, który działa identycznie wszędzie.
Ten post omawia, jak używać Docker Compose do odtwarzalnego testowania E2E lokalnie i w CI oraz jak uruchamiać testy smoke przeciwko efemerycznym środowiskom preview.
Dlaczego Docker dla testów E2E
Odtwarzalność — Ten sam kontener działa na laptopach developerów, runnerach CI i serwerach produkcyjnych. Koniec z “działa na moim komputerze”.
Izolacja — Każde uruchomienie testu startuje z czystego stanu. Brak pozostałości danych z poprzednich uruchomień.
Równoległość — Uruchamiaj wiele izolowanych środowisk jednocześnie dla równoległego wykonywania testów.
Prostota — Developerzy uruchamiają docker compose up zamiast ręcznie instalować Postgres, Redis i Elasticsearch.
:::tip[Docker vs. mocki] Mockowanie zewnętrznych zależności (baz danych, kolejek wiadomości) w testach jest szybkie, ale kruche. Mocki odstają od prawdziwego zachowania. Docker pozwala testować przeciwko prawdziwej bazie danych z minimalnym narzutem. :::
Docker Compose dla lokalnego E2E
Docker Compose orkiestruje aplikacje wielokontenerowe. Zdefiniuj swoją aplikację, bazę danych i zależności w docker-compose.yml.
Przykład: aplikacja webowa + Postgres
# docker-compose.yml
version: '3.8'
services:
db:
image: postgres:16
environment:
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
POSTGRES_DB: testdb
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U testuser"]
interval: 5s
timeout: 5s
retries: 5
app:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgresql://testuser:testpass@db:5432/testdb
NODE_ENV: test
depends_on:
db:
condition: service_healthy
Kluczowe funkcje:
- Serwis
dburuchamia Postgres 16 healthcheckzapewnia, że Postgres jest gotowy przed startem aplikacji- Serwis
appczeka nadbdo bycia zdrowym (depends_onzcondition) - Zmienne środowiskowe skonfigurowane dla trybu testowego
Uruchamianie testów E2E z Docker Compose
# Uruchom serwisy
docker compose up -d
# Poczekaj, aż aplikacja będzie gotowa
npx wait-on http://localhost:3000
# Uruchom testy Playwright
npm run test:e2e
# Zatrzymaj serwisy
docker compose down
Alternatywnie, skryptuj to w package.json:
{
"scripts": {
"test:e2e:docker": "docker compose up -d && npx wait-on http://localhost:3000 && npm run test:e2e; docker compose down"
}
}
:::warning[Persystencja danych]
Domyślnie Docker Compose persystuje dane w wolumenach. Dla testów chcesz czystego stanu przy każdym uruchomieniu. Użyj docker compose down -v aby usunąć wolumeny i zresetować stan.
:::
Docker Compose w CI
Ten sam docker-compose.yml działa w GitHub Actions.
Workflow GitHub Actions z Docker Compose
name: E2E Tests
on: [pull_request]
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- name: Start services with Docker Compose
run: docker compose up -d
- name: Wait for app to be ready
run: npx wait-on http://localhost:3000 --timeout 60000
- name: Install Playwright browsers
run: npx playwright install --with-deps chromium
- name: Run E2E tests
run: npm run test:e2e
- name: Stop services
if: always()
run: docker compose down -v
- name: Upload test report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
Dlaczego to działa:
docker compose up -dstartuje serwisy w tlewait-onblokuje, aż aplikacja odpowiada nahttp://localhost:3000- Playwright uruchamia testy przeciwko
http://localhost:3000 docker compose down -vsprząta kontenery i wolumeny (if: always()zapewnia sprzątanie nawet jeśli testy failują)
:::info[Wydajność CI] Startowanie kontenerów Docker dodaje 20–40 sekund do uruchomień CI. To akceptowalne dla testów E2E, ale zbyt wolne dla testów jednostkowych. Używaj Dockera tylko dla suitów testów integracyjnych i E2E. :::
Seedowanie danych testowych
Testy E2E potrzebują realistycznych danych. Zasiej bazę danych przed uruchomieniem testów.
Skrypt seedowania bazy danych
// scripts/seed-test-db.ts
import { Client } from 'pg';
const client = new Client({
connectionString: process.env.DATABASE_URL,
});
async function seed() {
await client.connect();
await client.query(`
INSERT INTO users (email, name, role)
VALUES
('admin@test.com', 'Admin User', 'admin'),
('user@test.com', 'Regular User', 'user');
`);
await client.query(`
INSERT INTO products (name, price, stock)
VALUES
('Product A', 29.99, 100),
('Product B', 49.99, 50);
`);
await client.end();
console.log('Test database seeded');
}
seed().catch(console.error);
Uruchom seedowanie po starcie serwisów:
- name: Start services
run: docker compose up -d
- name: Wait for app
run: npx wait-on http://localhost:3000
- name: Seed test data
run: npm run seed:test
- name: Run E2E tests
run: npm run test:e2e
:::tip[Idempotentne seedy]
Rób skrypty seedowania idempotentne — bezpieczne do wielokrotnego uruchamiania. Użyj INSERT ... ON CONFLICT DO NOTHING lub DELETE FROM table przed insertem. To zapobiega niepowodzeniom przy wielokrotnym uruchamianiu testów podczas developmentu.
:::
Efemeryczne środowiska preview
Środowiska efemeryczne to tymczasowe deploymenty tworzone dla każdego pull requesta. Pozwalają testować aplikację w środowisku podobnym do produkcyjnego przed merge’em.
Serwisy jak Vercel, Netlify i Heroku Review Apps automatycznie deployują środowiska preview dla każdego PR.
Deploymenty preview Vercel
Vercel automatycznie deployuje każdy push na unikalny URL:
https://my-app-pr123-abc1234.vercel.app
Ten URL jest publikowany jako komentarz w pull requeście.
Uruchamianie testów smoke przeciwko deploymentom preview
Poczekaj na zakończenie deploymentu preview, potem uruchom testy smoke przeciwko niemu.
name: Preview E2E
on:
pull_request:
jobs:
preview-smoke-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- name: Wait for Vercel deployment
uses: UnlyEd/github-action-await-vercel@v1
with:
deployment-url: ${{ github.event.pull_request.head.sha }}
timeout: 300
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Run smoke tests against preview
env:
BASE_URL: ${{ steps.await-vercel.outputs.url }}
run: npm run test:e2e:smoke
- name: Upload test report
if: always()
uses: actions/upload-artifact@v4
with:
name: preview-smoke-report
path: playwright-report/
Kluczowe funkcje:
- Akcja
await-vercelczeka na zakończenie deploymentu Vercel - Zmienna środowiskowa
BASE_URLkieruje testy na URL preview - Testy smoke działają przeciwko deployowanemu preview, nie localhost
Konfigurowanie Playwrighta dla dynamicznego Base URL
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
},
projects: [
{
name: 'chromium',
use: { browserName: 'chromium' },
},
],
});
Teraz testy używają URL preview, gdy BASE_URL jest ustawiony:
test('homepage loads', async ({ page }) => {
await page.goto('/'); // Używa baseURL z configu
await expect(page.locator('h1')).toContainText('Welcome');
});
:::warning[Ograniczenia środowisk preview] Środowiska preview często używają oddzielnych baz danych lub mockowych danych. Nie zakładaj parytetu danych z produkcją. Testuj renderowanie UI, nawigację i autentykację — nie integralność danych produkcyjnych. :::
Porównanie lokalnego Dockera vs. środowisk preview
| Aspekt | Lokalny Docker | Środowisko preview |
|---|---|---|
| Szybkość | 20–40s startup | 60–180s deployment |
| Izolacja | Pełna kontrola nad serwisami | Zarządzane przez chmurę, ograniczona kontrola |
| Dane | Skrypty seed, pełna kontrola | Dane mockowe lub oddzielne testowe DB |
| Przypadek użycia | Testy integracyjne/E2E w CI | Testy smoke na prawdziwej infrastrukturze |
| Koszt | Darmowy (działa na runnerze CI) | Zależy od tieru platformy |
Rekomendacja: Używaj Docker Compose dla pełnych suitów testów E2E w CI. Używaj środowisk preview dla lekkich testów smoke, które walidują deployment i routing.
Praktyczny przykład — pełny workflow E2E CI
Połącz Docker Compose dla lokalnego testowania i testy smoke preview w jednym workflow.
name: E2E Tests
on: [pull_request]
jobs:
e2e-local:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- name: Start services
run: docker compose up -d
- name: Wait for app
run: npx wait-on http://localhost:3000 --timeout 60000
- name: Seed test data
run: npm run seed:test
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Run full E2E suite
run: npm run test:e2e
- name: Stop services
if: always()
run: docker compose down -v
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-report
path: playwright-report/
preview-smoke:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- name: Wait for Vercel
id: await-vercel
uses: UnlyEd/github-action-await-vercel@v1
with:
deployment-url: ${{ github.event.pull_request.head.sha }}
timeout: 300
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Run smoke tests
env:
BASE_URL: ${{ steps.await-vercel.outputs.url }}
run: npm run test:e2e:smoke
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: preview-smoke-report
path: playwright-report/
Dwa joby:
- e2e-local — Pełna suita E2E przeciwko serwissom Docker Compose
- preview-smoke — Testy smoke przeciwko deploymentowi preview Vercel
Oba działają równolegle. Całkowity czas CI: ~5–7 minut.
Podsumowanie — środowiska jako kod
Odtwarzalne środowiska testowe to fundament niezawodnego CI/CD. Docker Compose czyni trywialnym pakowanie aplikacji, zależności i danych testowych w przenośną, wersjonowaną definicję środowiska.
Zasady:
- Używaj Docker Compose dla lokalnych i CI testów E2E
- Seeduj dane testowe w powtarzalny, idempotentny sposób
- Sprzątaj kontenery i wolumeny po każdym uruchomieniu
- Uruchamiaj testy smoke przeciwko deploymentom preview, aby walidować prawdziwą infrastrukturę
- Separuj odpowiedzialności — pełne E2E w Dockerze, testy smoke w preview
Gdy “działa na moim komputerze” staje się “działa w kontenerze”, niepowodzenia CI stają się możliwe do działania i godne zaufania.
Zadanie na ten tydzień: Stwórz docker-compose.yml dla swojego projektu z aplikacją i jej główną zależnością (baza danych, cache, itp.). Uruchom jeden test E2E przeciwko niemu lokalnie. Zmierz czas od docker compose up do zakończenia testu.
Dalej w tej serii: Część 4 — Raporty, artefakty i triage awarii w CI