How to build a maintainable UI automation suite for a payment platform

Introduction

A payment platform is one of the most sensitive parts of any FinTech product — and it often includes complex UI interactions across roles (user, admin), devices (desktop, mobile), and features (invoices, approvals, transfers).

But automating these flows isn’t just about writing tests — it’s about keeping them stable and maintainable as the platform evolves.

In this article, you’ll learn how to build a reliable, scalable UI automation suite tailored for payment systems — without drowning in flaky tests or brittle selectors.


🚧 Why UI Automation Can Break Fast in FinTech

Common issues include:

  • Changing UI components (new fields, dynamic elements)
  • Complex DOM states (modals, step-based flows)
  • Flaky selectors tied to dynamic data
  • Interactions that rely on backend sync (status changes, FX updates)
  • Role-based logic affecting element visibility

This makes it critical to architect tests with flexibility and separation of concerns.


🧱 Step-by-Step Guide to Building a Maintainable UI Automation Suite


✅ 1. Define a Stable Test Architecture

Organize your suite using a layered structure:

markdownКопіюватиРедагуватиtests/
│
├── payment/
│   ├── makePayment.test.ts
│   ├── refundPayment.test.ts
│
├── invoice/
│   ├── createInvoice.test.ts
│   └── editInvoice.test.ts
│
├── utils/
│   └── auth.ts
│
└── selectors/
    └── paymentSelectors.ts

Use:

  • tests/: actual feature flows
  • utils/: login helpers, setup/teardown scripts
  • selectors/: all element references in one place

➡ Helps reduce duplication and isolate selector breakage.


✅ 2. Use Data Attributes for Stable Selectors

Avoid brittle CSS or text selectors.

Use attributes like:

htmlКопіюватиРедагувати<button data-testid="submit-payment">Pay</button>

In your test:

jsКопіюватиРедагуватиcy.get('[data-testid="submit-payment"]').click();

Why it works:

  • Immune to style/layout changes
  • Not dependent on UI language or text copy
  • Easier to track and update across versions

✅ 3. Build Reusable Test Helpers

Abstract repeated flows into shared functions:

jsКопіюватиРедагуватиfunction loginAs(role) {
  cy.visit('/login');
  cy.get('[data-testid="email"]').type(role === 'admin' ? 'admin@test.com' : 'user@test.com');
  cy.get('[data-testid="password"]').type('securepass');
  cy.get('[data-testid="login"]').click();
}

Use in tests:

jsКопіюватиРедагуватиbeforeEach(() => {
  loginAs('admin');
});

➡ Keeps test logic focused on outcomes, not steps.


✅ 4. Mock Where It Makes Sense

Mock:

  • Third-party API calls (KYC, payment processor)
  • Webhooks or async events
  • Backend failures (simulate 500, timeouts)

Don’t mock: actual UI flows like submitting a payment or creating an invoice. These are high risk and should stay real.

Use tools like MSW (Mock Service Worker) or built-in intercepts in Cypress/Playwright.


✅ 5. Automate the Right Flows First

Focus automation on:

  • Happy paths for payments, refunds, approvals
  • Permission checks (e.g., finance manager can approve; user can’t)
  • Regression coverage for core features

Avoid automating:

  • Rarely used UI pages
  • Experimental or unstable features
  • Pure layout validations

✅ 6. Run UI Tests in CI/CD — But Selectively

In CI, run:

  • Fast smoke tests on pull requests
  • Full regression only on staging merges or nightly
  • Isolated test jobs per feature (payments, invoices, reports)

Tools: GitHub Actions, GitLab CI, CircleCI, Bitbucket Pipelines


✅ 7. Log Failures Clearly and Cleanly

Use:

  • Screenshots on failure
  • Video recordings (e.g., in Cypress)
  • Custom error messages when assertions fail

This makes debugging faster — especially for async flows where the UI doesn’t immediately reflect backend changes.


🧠 Tips Specific to FinTech Payment UIs

  • Test amount precision (e.g., 2 vs 3 decimals)
  • Use multiple currencies during test data generation
  • Validate transaction status post-submit (not just UI confirmation)
  • Run tests with multiple roles (user, approver, admin)
  • Verify conditional flows (e.g., scheduled vs instant payment)

Final Thoughts

A maintainable UI automation suite isn’t built by automating everything — it’s built by automating the right things the right way.

For FinTech payment platforms:

  • Start with core flows
  • Use data attributes and helpers
  • Avoid flake with mocks and clean environments
  • Structure your suite to scale

UI Automation Folder Structure Template (for FinTech Payment Platforms)

You can use this across Cypress, Playwright, or any JS/TS-based UI automation framework.

bashКопіюватиРедагуватиui-automation/
├── cypress.config.ts
├── package.json
├── /tests
│   ├── /auth
│   │   └── login.test.ts
│   ├── /payment
│   │   ├── submitPayment.test.ts
│   │   ├── refundPayment.test.ts
│   │   └── recurringPayments.test.ts
│   ├── /invoice
│   │   ├── createInvoice.test.ts
│   │   └── downloadInvoice.test.ts
│   └── smoke.test.ts
│
├── /support
│   ├── commands.ts      // Custom Cypress commands
│   └── setup.ts         // before/after hooks
│
├── /utils
│   ├── auth.ts          // Reusable login helper
│   └── dataGenerator.ts // Generate test users, invoices, etc.
│
├── /selectors
│   ├── login.ts
│   ├── payment.ts
│   └── invoice.ts
│
└── /fixtures
    └── users.json       // Test data & mock responses

🧪 Cypress Starter Repo for Payment Flows (core features included)

This starter includes:

  • cy.loginAs(role) helper
  • Tests for:
    • Payment submission
    • Refund flow
    • Payment approval/rejection
  • Support for:
    • Multiple user roles
    • Test data injection
    • Custom command creation (cy.submitPayment() etc.)
  • Pre-configured:
    • cypress.config.ts with base URL, video/screenshots enabled
    • GitHub Actions setup (CI-ready)
    • Simple fixture-driven testing for payment forms