Web Development

Cypress: End-to-End Testing

Tutorial lengkap Cypress E2E Testing — commands, assertions, fixtures, custom commands, intercepting API, CI/CD integration, dan best practices untuk testing otomatis

1. Pengenalan Cypress

Cypress adalah framework testing end-to-end (E2E) open-source yang dibangun khusus untuk web modern. Dikembangkan oleh Cypress.io, tool ini berjalan langsung di browser (bukan Selenium) sehingga memberikan pengalaman testing yang lebih cepat, andal, dan mudah di-debug.

Berbeda dengan Selenium WebDriver yang mengirim perintah melalui jaringan, Cypress berjalan di dalam event loop yang sama dengan aplikasi Anda. Ini memberikan kontrol penuh terhadap browser, network, dan DOM — menghasilkan test yang lebih stabil dan tidak flaky.

Keunggulan Cypress

Keunggulan Penjelasan
Time TravelScreenshot otomatis di setiap langkah — lihat state saat test berjalan
Real-time ReloadTest otomatis re-run saat file diubah
Debugging MudahGunakan DevTools langsung, error messages yang jelas
Automatic WaitingTidak perlu manual waits/polling — Cypress menunggu otomatis
Network StubbingIntercept dan mock HTTP requests dengan mudah
Screenshots & VideosRekam video dan screenshot otomatis saat test gagal
Cross-browserChrome, Firefox, Edge, Electron
Developer ExperienceAPI yang intuitif, dokumentasi luar biasa

Cypress vs Selenium vs Playwright

Aspek Cypress Selenium Playwright
ArchitectureIn-browserWebDriverCDP Protocol
BahasaJavaScriptMulti-languageMulti-language
Speed🟢 Cepat🟡 Sedang🟢 Cepat
Debugging🟢 Sangat baik🟡 Dasar🟢 Trace Viewer
Parallel🟡 Perlu Dashboard🟢 Bawaan🟢 Bawaan
iFrame Support🔴 Terbatas🟢 Baik🟢 Baik
Learning Curve🟢 Mudah🟡 Sedang🟢 Mudah
Diagram: Arsitektur Cypress
┌──────────────────────────────────────────────────────┐
│                 CYPRESS TEST RUNNER                  │
│                                                      │
│  ┌────────────────────────────────────────────────┐  │
│  │             Browser (Chrome/Firefox)           │  │
│  │                                                │  │
│  │  ┌──────────────┐    ┌──────────────────────┐  │  │
│  │  │  Your Web    │    │  Cypress Runner      │  │  │
│  │  │  Application │◄──►│  (Same origin)       │  │  │
│  │  │              │    │                      │  │  │
│  │  │  DOM Events  │    │  • cy.get()          │  │  │
│  │  │  Network     │    │  • cy.click()        │  │  │
│  │  │  Cookies     │    │  • cy.intercept()    │  │  │
│  │  └──────────────┘    │  • Assertions        │  │  │
│  │                      └──────────────────────┘  │  │
│  └────────────────────────────────────────────────┘  │
│                                                      │
│  ┌────────────────────────────────────────────────┐  │
│  │              Node.js Server                    │  │
│  │  • File system access                          │  │
│  │  • Task execution                              │  │
│  │  • Plugin hooks                                │  │
│  └────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────┘

2. Instalasi & Setup

Instalasi

Bash
# Instal Cypress sebagai dev dependency
npm install cypress --save-dev

# Atau dengan yarn
yarn add cypress --dev

# Atau dengan pnpm
pnpm add cypress -D

# Buka Cypress Test Runner (GUI)
npx cypress open

# Jalankan test headless (untuk CI)
npx cypress run

# Jalankan test untuk browser tertentu
npx cypress run --browser chrome
npx cypress run --browser firefox

Struktur Folder Cypress

File Structure
project-root/
├── cypress/
│   ├── e2e/                    ← Test files (specs)
│   │   ├── auth/
│   │   │   ├── login.cy.js
│   │   │   └── register.cy.js
│   │   ├── dashboard/
│   │   │   └── dashboard.cy.js
│   │   └── homepage.cy.js
│   ├── fixtures/               ← Test data (JSON)
│   │   ├── users.json
│   │   ├── products.json
│   │   └── api-responses.json
│   ├── support/                ← Custom commands & utilities
│   │   ├── commands.js         ← Custom commands
│   │   ├── e2e.js             ← Global config (beforeEach, dll.)
│   │   └── helpers.js         ← Utility functions
│   └── downloads/              ← Downloaded files during tests
├── cypress.config.js           ← Cypress configuration
└── package.json

Konfigurasi Cypress

JavaScript — cypress.config.js
const { defineConfig } = require('cypress');

module.exports = defineConfig({
  // E2E testing config
  e2e: {
    baseUrl: 'http://localhost:3000',
    specPattern: 'cypress/e2e/**/*.cy.{js,jsx,ts,tsx}',
    supportFile: 'cypress/support/e2e.js',

    // Timeouts
    defaultCommandTimeout: 10000,    // 10 detik
    requestTimeout: 15000,           // 15 detik untuk cy.request()
    responseTimeout: 30000,          // 30 detik untuk server response
    pageLoadTimeout: 60000,          // 60 detik untuk page load

    // Screenshots & Videos
    screenshotOnRunFailure: true,
    video: true,
    videosFolder: 'cypress/videos',
    screenshotsFolder: 'cypress/screenshots',

    // Viewport
    viewportWidth: 1280,
    viewportHeight: 720,

    // Retries (berguna di CI)
    retries: {
      runMode: 2,      // Retry 2x saat run headless (CI)
      openMode: 0,      // Tidak retry saat open (development)
    },

    // Experimental features
    experimentalStudio: true,

    // Setup node events (plugins)
    setupNodeEvents(on, config) {
      // Implementasi plugin di sini
      on('task', {
        // Custom task untuk seed database
        async seedDatabase() {
          // Implementasi seed...
          return null;
        },
        // Log ke terminal
        log(message) {
          console.log(message);
          return null;
        },
      });

      return config;
    },
  },
});

3. Commands: Perintah Dasar

Cypress menggunakan chained commands yang membaca seperti kalimat bahasa Inggris. Setiap command mengembalikan elemen yang bisa di-chain ke command berikutnya.

Querying Elements

JavaScript — cypress/e2e/querying.cy.js
describe('Querying Elements', () => {
  beforeEach(() => {
    cy.visit('/halaman-produk');
  });

  it('berhasil menemukan elemen dengan berbagai selector', () => {
    // ═══ cy.get() — Selector utama ═══
    cy.get('.judul-produk');           // CSS class
    cy.get('#produk-1');               // ID
    cy.get('[data-cy="add-to-cart"]'); // Data attribute (recommended!)
    cy.get('button');                   // Tag name
    cy.get('[type="submit"]');         // Attribute selector
    cy.get('.kartu h3');               // Descendant selector

    // ═══ cy.contains() — Cari berdasarkan teks ═══
    cy.contains('Tambah ke Keranjang');
    cy.contains('button', 'Beli');     // Elemen tertentu + teks
    cy.contains(/harga/i);             // Regex

    // ═══ cy.find() — Cari di dalam elemen yang sudah dipilih ═══
    cy.get('.product-card')
      .find('.harga');

    // ═══ Within a specific area ═══
    cy.get('.sidebar')
      .within(() => {
        cy.get('a').should('have.length.gte', 3);
        cy.contains('Settings').click();
      });
  });
});

Actions (User Interactions)

JavaScript — cypress/e2e/actions.cy.js
describe('User Actions', () => {
  beforeEach(() => {
    cy.visit('/form-demo');
  });

  it('berhasil mengisi dan mengirim form', () => {
    // ═══ cy.type() — Ketik teks ═══
    cy.get('[data-cy="input-nama"]')
      .type('Budi Santoso')
      .should('have.value', 'Budi Santoso');

    cy.get('[data-cy="input-email"]')
      .type('budi@example.com');

    // Type dengan opsi khusus
    cy.get('[data-cy="input-password"]')
      .type('Rahasia123!', { delay: 50 }); // delay antar karakter

    // ═══ cy.clear() — Hapus input ═══
    cy.get('[data-cy="input-search"]')
      .clear()
      .type('query baru');

    // ═══ cy.click() — Klik elemen ═══
    cy.get('[data-cy="btn-submit"]').click();
    cy.get('.card').first().click();
    cy.get('.card').last().click();
    cy.get('.card').eq(2).click(); // Index ke-3

    // Double click
    cy.get('[data-cy="editable"]').dblclick();

    // Right click
    cy.get('[data-cy="context-menu"]').rightclick();

    // ═══ cy.select() — Dropdown select ═══
    cy.get('[data-cy="select-kategori"]')
      .select('elektronik');       // Berdasarkan value

    cy.get('[data-cy="select-kota"]')
      .select('Jakarta');           // Berdasarkan text

    // ═══ cy.check() / cy.uncheck() — Checkbox ═══
    cy.get('[data-cy="checkbox-setuju"]')
      .check()
      .should('be.checked');

    cy.get('[data-cy="checkbox-setuju"]')
      .uncheck()
      .should('not.be.checked');

    // Check multiple checkboxes
    cy.get('.checkbox-group input')
      .check(['react', 'vue', 'angular']);

    // ═══ Radio buttons ═══
    cy.get('[data-cy="radio-gold"]')
      .check()
      .should('be.checked');

    // ═══ Scroll ═══
    cy.scrollTo('bottom');
    cy.get('#footer').scrollIntoView();

    // ═══ File upload ═══
    cy.get('[data-cy="file-input"]')
      .selectFile('cypress/fixtures/test-image.png');

    // Drag and drop
    cy.get('[data-cy="drag-source"]')
      .drag('[data-cy="drop-target"]');
  });
});

Navigation & Window

JavaScript — Navigation Commands
describe('Navigation', () => {
  it('berhasil navigasi halaman', () => {
    // Kunjungi halaman
    cy.visit('/');
    cy.visit('/dashboard');
    cy.visit('http://localhost:3000/profile');

    // Visit dengan auth headers
    cy.visit('/admin', {
      headers: {
        Authorization: 'Bearer token123',
      },
    });

    // Navigasi browser
    cy.go('back');
    cy.go('forward');
    cy.reload();

    // URL assertions
    cy.url().should('include', '/dashboard');
    cy.url().should('eq', 'http://localhost:3000/dashboard');
    cy.location('pathname').should('eq', '/dashboard');

    // Cookies
    cy.setCookie('session', 'abc123');
    cy.getCookie('session').should('have.property', 'value', 'abc123');
    cy.clearCookies();

    // Local Storage
    cy.window().then((win) => {
      win.localStorage.setItem('token', 'xyz789');
    });
    cy.clearLocalStorage();

    // Title
    cy.title().should('eq', 'Dashboard - BeebaneLabs');
  });
});

4. Assertions

Cypress menggunakan Chai BDD assertions secara bawaan, plus Sinon untuk spies/stubs. Assertions ditulis secara natural dan mendukung chaining.

Chai & Chai-jQuery Assertions

JavaScript — cypress/e2e/assertions.cy.js
describe('Assertions', () => {
  beforeEach(() => {
    cy.visit('/dashboard');
  });

  it('berhasil menggunakan berbagai assertion', () => {
    // ═══ Implicit Assertions (should) ═══

    // Visibility & Existence
    cy.get('[data-cy="judul"]').should('be.visible');
    cy.get('[data-cy="hidden-element"]').should('not.be.visible');
    cy.get('[data-cy="card"]').should('exist');
    cy.get('[data-cy="nonexistent"]').should('not.exist');

    // Text content
    cy.get('[data-cy="judul"]')
      .should('have.text', 'Dashboard Utama');        // Exact match
    cy.get('[data-cy="judul"]')
      .should('contain.text', 'Dashboard');            // Partial match
    cy.get('[data-cy="judul"]')
      .should('match', /dashboard/i);                  // Regex

    // CSS & Classes
    cy.get('[data-cy="card"]')
      .should('have.class', 'active');
    cy.get('[data-cy="card"]')
      .should('have.css', 'color', 'rgb(255, 255, 255)');
    cy.get('[data-cy="card"]')
      .should('have.css', 'background-color')
      .and('not.eq', 'rgb(0, 0, 0)');

    // Attributes
    cy.get('[data-cy="link"]')
      .should('have.attr', 'href', '/profile');
    cy.get('[data-cy="input"]')
      .should('have.attr', 'placeholder', 'Cari...')
      .and('have.attr', 'required');

    // Value (for inputs)
    cy.get('[data-cy="search"]')
      .type('query')
      .should('have.value', 'query');

    // Length & Count
    cy.get('.product-card')
      .should('have.length.gte', 1);       // >= 1
    cy.get('.product-card')
      .should('have.length', 6);            // Exactly 6
    cy.get('.product-card')
      .should('have.length.lte', 10);       // <= 10

    // Chain multiple assertions
    cy.get('[data-cy="card"]')
      .should('be.visible')
      .and('have.class', 'card')
      .and('contain.text', 'Produk');

    // ═══ Explicit Assertions ═══
    cy.get('[data-cy="harga"]').then(($el) => {
      // Chai expect
      expect($el.text()).to.equal('Rp 150.000');
      expect($el).to.have.class('harga');
      expect($el).to.be.visible;
    });

    // Assert on subject
    cy.get('[data-cy="count"]').invoke('text').then((text) => {
      expect(parseInt(text)).to.be.greaterThan(0);
      expect(parseInt(text)).to.be.lessThan(100);
    });
  });
});

5. Fixtures & Data Testing

Fixtures adalah file data (biasanya JSON) yang digunakan untuk test data yang konsisten. Ini memungkinkan test Anda tidak bergantung pada data production atau state database.

Membuat dan Menggunakan Fixtures

JSON — cypress/fixtures/users.json
{
  "admin": {
    "nama": "Admin Beebane",
    "email": "admin@beebane.com",
    "password": "Admin123!",
    "role": "admin"
  },
  "user": {
    "nama": "Budi Santoso",
    "email": "budi@example.com",
    "password": "User123!",
    "role": "user"
  },
  "premium": {
    "nama": "Sari Dewi",
    "email": "sari@example.com",
    "password": "Premium123!",
    "role": "premium"
  },
  "invalid": {
    "email": "salah@example.com",
    "password": "password_salah"
  }
}
JSON — cypress/fixtures/products.json
{
  "products": [
    {
      "id": 1,
      "nama": "Laptop ASUS ROG",
      "harga": 15000000,
      "kategori": "elektronik",
      "stok": 25
    },
    {
      "id": 2,
      "nama": "Keyboard Mechanical",
      "harga": 750000,
      "kategori": "aksesoris",
      "stok": 100
    },
    {
      "id": 3,
      "nama": "Monitor 4K 27 inch",
      "harga": 5500000,
      "kategori": "elektronik",
      "stok": 0
    }
  ]
}
JavaScript — Using Fixtures in Tests
describe('Menggunakan Fixtures', () => {
  // Load fixture sebelum semua test
  beforeEach(() => {
    // Load dan alias fixture
    cy.fixture('users').as('usersData');
    cy.fixture('products').as('productsData');
  });

  it('login dengan data dari fixture', function () {
    // Akses data via alias (gunakan function, BUKAN arrow function!)
    const { email, password } = this.usersData.user;

    cy.visit('/login');
    cy.get('[data-cy="input-email"]').type(email);
    cy.get('[data-cy="input-password"]').type(password);
    cy.get('[data-cy="btn-login"]').click();

    cy.url().should('include', '/dashboard');
  });

  it('menampilkan produk dari fixture', function () {
    const { products } = this.productsData;

    cy.visit('/produk');

    products.forEach((product, index) => {
      cy.get('.product-card')
        .eq(index)
        .should('contain.text', product.nama)
        .and('contain.text', `Rp ${product.harga.toLocaleString('id-ID')}`);
    });
  });

  // Atau load fixture langsung di test
  it('gunakan cy.intercept dengan fixture', () => {
    cy.fixture('products').then((data) => {
      // Mock API response
      cy.intercept('GET', '/api/products', {
        statusCode: 200,
        body: data.products,
      }).as('getProducts');
    });

    cy.visit('/produk');
    cy.wait('@getProducts');

    cy.get('.product-card').should('have.length', 3);
  });
});

6. Intercepting API Requests

cy.intercept() memungkinkan Anda menangkap, memodifikasi, dan mock network requests. Ini sangat penting untuk E2E testing karena memungkinkan Anda mengisolasi frontend dari backend.

Intercept Patterns

JavaScript — cypress/e2e/api-intercept.cy.js
describe('API Intercepting', () => {
  it('mock API response', () => {
    // Mock GET request
    cy.intercept('GET', '/api/users', {
      statusCode: 200,
      body: [
        { id: 1, nama: 'Budi' },
        { id: 2, nama: 'Sari' },
      ],
    }).as('getUsers');

    cy.visit('/users');
    cy.wait('@getUsers');

    cy.get('.user-card').should('have.length', 2);
  });

  it('mock error response', () => {
    cy.intercept('GET', '/api/products', {
      statusCode: 500,
      body: { error: 'Server Error' },
    }).as('getProductsError');

    cy.visit('/products');
    cy.wait('@getProductsError');

    cy.get('.error-message')
      .should('be.visible')
      .and('contain.text', 'Terjadi kesalahan');
  });

  it('intercept dan verifikasi request', () => {
    // Intercept POST request dan verifikasi body-nya
    cy.intercept('POST', '/api/orders').as('createOrder');

    cy.visit('/checkout');
    cy.get('[data-cy="btn-order"]').click();

    cy.wait('@createOrder').then((interception) => {
      // Verifikasi request body
      expect(interception.request.body).to.have.property('items');
      expect(interception.request.body.items).to.have.length.greaterThan(0);

      // Verifikasi response
      expect(interception.response.statusCode).to.eq(201);
    });
  });

  it('modifikasi request sebelum dikirim', () => {
    // Tambahkan header ke request
    cy.intercept('GET', '/api/data', (req) => {
      req.headers['Authorization'] = 'Bearer mock-token';
      req.continue(); // Lanjutkan request dengan modifikasi
    }).as('getData');

    cy.visit('/protected');
    cy.wait('@getData');
  });

  it('delay response untuk test loading state', () => {
    cy.intercept('GET', '/api/slow-data', (req) => {
      req.reply({
        delay: 3000, // Delay 3 detik
        body: { data: 'loaded' },
      });
    }).as('slowData');

    cy.visit('/slow-page');

    // Loading indicator should be visible
    cy.get('.loading-spinner').should('be.visible');

    cy.wait('@slowData');

    // Loading should disappear
    cy.get('.loading-spinner').should('not.exist');
    cy.get('.data-content').should('contain.text', 'loaded');
  });
});

7. Custom Commands

Custom Commands memungkinkan Anda membuat perintah reusable yang bisa digunakan di semua test. Ini sangat berguna untuk operasi yang sering dilakukan seperti login, navigasi, dan setup data.

JavaScript — cypress/support/commands.js
// ═══════════════════════════════════════
// Custom Commands — cypress/support/commands.js
// ═══════════════════════════════════════

// Login via UI
Cypress.Commands.add('loginViaUI', (email, password) => {
  cy.session([email, password], () => {
    cy.visit('/login');
    cy.get('[data-cy="input-email"]').type(email);
    cy.get('[data-cy="input-password"]').type(password);
    cy.get('[data-cy="btn-login"]').click();
    cy.url().should('include', '/dashboard');
  });
});

// Login via API (lebih cepat)
Cypress.Commands.add('loginViaAPI', (email, password) => {
  cy.request({
    method: 'POST',
    url: '/api/auth/login',
    body: { email, password },
  }).then((response) => {
    expect(response.status).to.eq(200);
    window.localStorage.setItem('auth-token', response.body.token);
    window.localStorage.setItem('user', JSON.stringify(response.body.user));
  });
});

// Select element by data-cy attribute (convenience)
Cypress.Commands.add('getByCy', (selector) => {
  return cy.get(`[data-cy="${selector}"]`);
});

// Select element by test-id
Cypress.Commands.add('getByTestId', (selector) => {
  return cy.get(`[data-testid="${selector}"]`);
});

// Upload gambar
Cypress.Commands.add('uploadImage', (selector, fileName) => {
  cy.get(selector).selectFile(`cypress/fixtures/images/${fileName}`);
});

// Seed database via API
Cypress.Commands.add('seedDatabase', () => {
  cy.task('seedDatabase');
});

// Verifikasi toast notification
Cypress.Commands.add('expectToast', (message, type = 'success') => {
  cy.get(`.toast.toast-${type}`)
    .should('be.visible')
    .and('contain.text', message);

  // Tunggu toast hilang
  cy.get(`.toast.toast-${type}`, { timeout: 5000 })
    .should('not.exist');
});

// Tunggu halaman selesai loading
Cypress.Commands.add('waitForPage', () => {
  cy.get('[data-cy="page-loading"]', { timeout: 10000 }).should('not.exist');
  cy.get('body').should('be.visible');
});

Menggunakan Custom Commands

JavaScript — cypress/e2e/dashboard.cy.js
import users from '../fixtures/users.json';

describe('Dashboard', () => {
  beforeEach(() => {
    // Login via API (cepat, tidak perlu UI)
    cy.loginViaAPI(users.admin.email, users.admin.password);
    cy.visit('/dashboard');
    cy.waitForPage();
  });

  it('menampilkan dashboard untuk admin', () => {
    cy.getByCy('judul-dashboard')
      .should('contain.text', 'Dashboard');

    cy.getByCy('stat-total-users')
      .should('be.visible');

    cy.getByCy('stat-total-orders')
      .should('be.visible');
  });

  it('admin bisa mengakses halaman manajemen', () => {
    cy.getByCy('nav-admin').click();
    cy.url().should('include', '/admin');
    cy.getByCy('admin-panel').should('be.visible');
  });

  it('menampilkan notifikasi', () => {
    cy.getByCy('btn-notifications').click();
    cy.getByCy('notification-panel')
      .should('be.visible')
      .within(() => {
        cy.get('.notification-item').should('have.length.gte', 1);
      });
  });
});

8. Testing Patterns

Page Object Pattern

JavaScript — cypress/pages/LoginPage.js
// Page Object: Login Page
class LoginPage {
  visit() {
    cy.visit('/login');
    return this;
  }

  fillEmail(email) {
    cy.getByCy('input-email').clear().type(email);
    return this;
  }

  fillPassword(password) {
    cy.getByCy('input-password').clear().type(password);
    return this;
  }

  submit() {
    cy.getByCy('btn-login').click();
    return this;
  }

  login(email, password) {
    this.fillEmail(email);
    this.fillPassword(password);
    this.submit();
    return this;
  }

  expectError(message) {
    cy.getByCy('error-message')
      .should('be.visible')
      .and('contain.text', message);
    return this;
  }

  expectRedirectTo(path) {
    cy.url().should('include', path);
    return this;
  }
}

export default new LoginPage();

// Penggunaan di test:
// import loginPage from '../pages/LoginPage';
// loginPage.visit().login('budi@example.com', 'Pass123!').expectRedirectTo('/dashboard');

9. CI/CD Integration

Cypress sangat mudah diintegrasikan dengan CI/CD platforms. Berikut adalah konfigurasi untuk beberapa platform populer:

GitHub Actions

YAML — .github/workflows/cypress.yml
name: Cypress E2E Tests

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  cypress-run:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build application
        run: npm run build

      - name: Run Cypress tests
        uses: cypress-io/github-action@v6
        with:
          start: npm start
          wait-on: 'http://localhost:3000'
          wait-on-timeout: 120
          browser: chrome
          record: true
        env:
          CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Upload screenshots (on failure)
        uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: cypress-screenshots
          path: cypress/screenshots

      - name: Upload videos
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: cypress-videos
          path: cypress/videos

Running Tests di Berbagai Mode

Bash — Perintah Cypress
# Buka Cypress Test Runner (GUI)
npx cypress open

# Jalankan semua test headless
npx cypress run

# Jalankan test file tertentu
npx cypress run --spec "cypress/e2e/login.cy.js"

# Jalankan dengan browser tertentu
npx cypress run --browser chrome
npx cypress run --browser firefox
npx cypress run --browser edge

# Jalankan dengan tag/heading tertentu (grep)
npx cypress run --env grep="login"

# Record ke Cypress Dashboard
npx cypress run --record --key your-record-key

# Parallel execution (butuh Cypress Dashboard)
npx cypress run --record --parallel --group "e2e-chrome" --browser chrome

# Jalankan dengan environment variables
npx cypress run --env API_URL=http://staging.example.com
💡 Tips CI/CD
  • Gunakan cy.session() untuk meng-cache login antar test
  • Aktifkan retries di CI untuk mengurangi flaky tests
  • Gunakan video: true untuk debug test yang gagal
  • Upload artifacts (screenshots/videos) ke CI pipeline
  • Pisahkan test suite: smoke tests (fast) vs full regression (thorough)
  • Gunakan environment variables untuk konfigurasi staging/production

10. Quiz Pemahaman

Uji pemahaman Anda tentang Cypress E2E Testing:

1. Mengapa Cypress lebih cepat dari Selenium?

2. Apa fungsi cy.intercept()?

3. Mengapa disarankan menggunakan [data-cy] sebagai selector?

4. Apa keuntungan cy.fixture() dalam testing?

5. Untuk apa cy.session() digunakan?

🔍 Zoom
100%
🎨 Tema