Laptop and charts representing performance testing

Browser-based Performance Testing with k6: Hands-on Guide

This write-up condenses the published DEV tutorial “Browser-based performance testing with K6” into an actionable walkthrough for frontend teams. Why k6 browser Simulates real browser interactions (Chromium) vs. protocol-only tests. Captures Web Vitals (LCP/INP/CLS) and UX signals (spinners, interactivity). Reuses familiar k6 scripting with async/await + locators. Setup Install k6 (latest) and ensure Node/npm are available. Project skeleton: k6-tests/ libs/ # flows (login, add to cart, checkout) pages/ # Page Object Model classes tests/ # test entry scripts utils/ # shared options (browser config, VUs, iterations) Configure browser scenario (utils/k6-options.js): export const browserOptions = { scenarios: { ui: { executor: "shared-iterations", vus: 1, iterations: 15, options: { browser: { type: "chromium" } }, }, }, }; Example flow (simplified) import { browser } from "k6/browser"; import { check } from "k6"; export const options = browserOptions; export default async function () { const page = await browser.newPage(); await page.goto("https://www.saucedemo.com/"); await page.locator("#user-name").type("standard_user"); await page.locator("#password").type("secret_sauce"); await page.locator("#login-button").click(); check(page, { "login ok": () => page.locator(".title").textContent() === "Products" }); await page.close(); } Running & reporting CLI run: k6 run ./tests/buyItemsUserA.js Live dashboard: K6_WEB_DASHBOARD=true K6_WEB_DASHBOARD_OPEN=true k6 run ... Export HTML report: K6_WEB_DASHBOARD=true K6_WEB_DASHBOARD_EXPORT=results/report.html Metrics to watch (aligns to Core Web Vitals) browser_web_vital_lcp — largest content paint; target < 2.5s p75. browser_web_vital_inp — interaction to next paint; target < 200ms p75. browser_web_vital_cls — layout shift; target < 0.1. Add custom checks for “spinner disappears”, “cart count”, “success message”. Tips Keep tests short and focused; one scenario per file. Reuse Page Objects for maintainability. Run with realistic VUs/iterations that mirror expected traffic. Use screenshots on failure for debuggability (page.screenshot()). Takeaway: k6 browser mode gives frontend teams reproducible, scriptable UX performance checks—ideal for catching regressions before they reach real users.

December 10, 2025 · 3924 views

Backend Testing Strategies: Unit, Integration, and E2E Tests

Comprehensive testing is essential for reliable backend systems. Here’s a guide to effective testing strategies. Testing Pyramid /\ / \ E2E Tests (Few) /____\ / \ Integration Tests (Some) /________\ / \ Unit Tests (Many) /____________\ 1. Unit Tests What to Test Individual functions Business logic Data transformations Edge cases Example describe('UserService', () => { it('should create user with valid data', () => { const userData = { email: '[email protected]', name: 'Test User' }; const user = userService.createUser(userData); expect(user.email).toBe('[email protected]'); expect(user.id).toBeDefined(); }); it('should throw error for duplicate email', () => { userService.createUser({ email: '[email protected]' }); expect(() => { userService.createUser({ email: '[email protected]' }); }).toThrow('Email already exists'); }); }); 2. Integration Tests What to Test API endpoints Database operations External service interactions Service integration Example describe('User API', () => { beforeEach(async () => { await db.migrate.latest(); }); afterEach(async () => { await db.migrate.rollback(); }); it('POST /api/users should create user', async () => { const response = await request(app) .post('/api/users') .send({ email: '[email protected]', name: 'Test User', password: 'password123' }) .expect(201); expect(response.body.email).toBe('[email protected]'); const user = await db('users').where({ email: '[email protected]' }).first(); expect(user).toBeDefined(); }); }); 3. End-to-End Tests What to Test Complete user workflows System integration Critical paths Example describe('User Registration Flow', () => { it('should complete full registration process', async () => { // Register user const registerResponse = await request(app) .post('/api/auth/register') .send({ email: '[email protected]', password: 'password123' }); expect(registerResponse.status).toBe(201); // Login const loginResponse = await request(app) .post('/api/auth/login') .send({ email: '[email protected]', password: 'password123' }); expect(loginResponse.status).toBe(200); expect(loginResponse.body.token).toBeDefined(); // Access protected route const profileResponse = await request(app) .get('/api/users/me') .set('Authorization', `Bearer ${loginResponse.body.token}`); expect(profileResponse.status).toBe(200); }); }); 4. Test Data Management Fixtures const userFixtures = { validUser: { email: '[email protected]', name: 'Test User', password: 'password123' }, adminUser: { email: '[email protected]', name: 'Admin', password: 'admin123', role: 'admin' } }; Factories function createUser(overrides = {}) { return { email: '[email protected]', name: 'Test User', password: 'password123', ...overrides }; } 5. Mocking External Services // Mock external API jest.mock('../services/paymentService', () => ({ processPayment: jest.fn().mockResolvedValue({ transactionId: 'tx_123', status: 'success' }) })); 6. Database Testing // Use test database const testDb = { host: process.env.TEST_DB_HOST, database: process.env.TEST_DB_NAME }; // Clean database between tests beforeEach(async () => { await db.raw('TRUNCATE TABLE users CASCADE'); }); 7. Test Coverage // Aim for high coverage // Focus on critical paths // Don't obsess over 100% // Use coverage tools // jest --coverage // nyc Best Practices Write tests first (TDD) Test behavior, not implementation Keep tests independent Use descriptive test names Test edge cases Mock external dependencies Clean up test data Run tests in CI/CD Maintain test code quality Review test failures Conclusion Effective testing requires: ...

July 20, 2024 · 3850 views