Skip to content

optivem/modern-acceptance-testing-in-legacy-code-typescript

Repository files navigation

Modern Acceptance Testing in Legacy Code (TypeScript)

acceptance-stage

Prerequisites

  • Node.js 22 (LTS)
  • Docker Desktop
  • PowerShell 7+

Check that you have Powershell 7

$PSVersionTable.PSVersion

Check that you have Node.js 22 installed

node -v

Inside monolith and system-test install dependencies

npm install

Inside system-test install:

npx playwright install

Run Everything

.\run.ps1 all

This will:

  1. Build the Monolith (compile TypeScript and create build)
  2. Start Docker containers (Monolith, PostgreSQL, & Simulated External Systems)
  3. Wait for services to be healthy
  4. Run all System Tests

You can open these URLs in your browser:

Separate Commands

Build

Compiles the code and creates the Docker image (local mode only):

.\run.ps1 build

Start Services

Starts the Docker containers:

# Local mode (uses locally built code)
.\run.ps1 start

# Pipeline mode (uses pre-built image from registry)
.\run.ps1 start pipeline

You can open these URLs in your browser:

Run Tests

.\run.ps1 test

View Logs

.\run.ps1 logs

Stop Services

.\run.ps1 stop

Client Layer Architecture

This project implements the Client Layer Pattern for test automation, providing a clean abstraction over Playwright API and UI interactions. The Client Layer separates test logic from implementation details and makes tests more maintainable.

Structure

system-test/
└── core/
    ├── clients/
    │   ├── commons/               # Shared test utilities
    │   │   ├── TestHttpClient.ts  # HTTP request wrapper
    │   │   └── TestPageClient.ts  # Browser interaction wrapper
    │   ├── system/                # System under test clients
    │   │   ├── api/               # API client
    │   │   │   ├── controllers/   # API endpoint controllers
    │   │   │   │   ├── EchoController.ts
    │   │   │   │   └── OrderController.ts
    │   │   │   ├── dtos/          # Data transfer objects
    │   │   │   │   ├── PlaceOrderRequest.ts
    │   │   │   │   ├── PlaceOrderResponse.ts
    │   │   │   │   ├── GetOrderResponse.ts
    │   │   │   │   └── OrderStatus.ts
    │   │   │   └── ShopApiClient.ts
    │   │   └── ui/                # UI client
    │   │       ├── pages/         # Page objects
    │   │       │   ├── HomePage.ts
    │   │       │   ├── NewOrderPage.ts
    │   │       │   └── OrderHistoryPage.ts
    │   │       └── ShopUiClient.ts
    │   ├── external/              # External system clients
    │   │   └── erp/
    │   │       ├── controllers/
    │   │       │   └── ProductController.ts
    │   │       ├── dtos/
    │   │       │   └── CreateProductResponse.ts
    │   │       └── ErpApiClient.ts
    │   ├── ClientFactory.ts       # Creates client instances
    │   └── Closer.ts        # Manages resource cleanup
    └── TestConfiguration.ts        # Test environment configuration

Key Components

1. Commons Layer

  • TestHttpClient: Wraps Playwright's API request functionality with methods like get(), post(), and assertion helpers (assertOk(), assertCreated(), etc.)
  • TestPageClient: Wraps Playwright's Page API with methods like fill(), click(), readTextContent(), providing consistent timeouts and error handling

2. System Clients

  • ShopApiClient: API client for the shop system with controller-based organization
  • ShopUiClient: UI client for browser-based testing with page object pattern
  • Controllers: Organize API endpoints (e.g., OrderController handles /orders endpoints)
  • Pages: Encapsulate UI page interactions (e.g., NewOrderPage handles the order form)

3. External Clients

  • ErpApiClient: Client for interacting with external ERP system
  • Allows tests to set up test data in external systems

4. DTOs (Data Transfer Objects)

  • Type-safe request/response objects
  • Ensures consistency between tests and API contracts

Usage Example

import { test } from '@playwright/test';
import { ClientFactory } from '../../core/clients/ClientFactory';
import { Closer } from '../../core/clients/Closer';

test('should place an order via API', async () => {
  // Arrange
  const shopApiClient = await ClientFactory.createShopApiClient();
  const erpApiClient = await ClientFactory.createErpApiClient();

  try {
    // Create product in ERP system
    const sku = await erpApiClient.products().createProduct('TEST-SKU', 99.99);

    // Act - Place order
    const response = await shopApiClient.orders().placeOrder(sku, '5', 'US');

    // Assert
    const orderResponse = await shopApiClient.orders()
      .assertOrderPlacedSuccessfully(response);
    
    expect(orderResponse.orderNumber).toMatch(/ORD-/);
  } finally {
    // Cleanup
    await Closer.close(shopApiClient);
    await Closer.close(erpApiClient);
  }
});

Benefits

  1. Separation of Concerns: Tests focus on business logic, not technical details
  2. Reusability: Clients and pages can be reused across multiple tests
  3. Maintainability: Changes to API/UI only require updating the client layer
  4. Type Safety: TypeScript ensures compile-time type checking
  5. Readability: Tests read like business scenarios
  6. Resource Management: Consistent cleanup with Closer

Reference

This Client Layer implementation is based on the Java reference project.

License

MIT License

This project is released under the MIT License.

Contributors

About

Modern Acceptance Testing in Legacy Code (TypeScript)

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •