CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl-labs/jest-testing

Jest testing patterns — test structure, mocking, async testing, snapshot

99

1.26x
Quality

99%

Does it follow best practices?

Impact

99%

1.26x

Average score across 6 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

task.mdevals/scenario-4/

Extend the Payment Service Test Suite

Problem/Feature Description

The billing team has a PaymentService that processes charges through an external payment gateway. A junior engineer wrote a starter test file but left it incomplete — it covers only the most basic happy path and is missing coverage for validation logic and error scenarios.

Your task is to extend the existing test file to achieve thorough coverage. The PaymentService has an internal validateAmount method that sanity-checks the charge amount before contacting the gateway. The team wants tests that verify this internal validation is called correctly during payment processing, without replacing the real implementation entirely, so any bugs introduced into validateAmount would still be caught.

Output Specification

Extend (or rewrite if necessary) the test file at src/billing/__tests__/paymentService.test.ts with a complete test suite.

Tests should cover at minimum:

  • Successful charge with a valid amount
  • Validation called with the correct amount during a charge
  • Rejection when the amount is zero or negative
  • Rejection when the payment gateway client throws
  • Verifying the gateway is not called when validation fails

Input Files

src/billing/paymentService.ts

import { GatewayClient } from '../gateway/gatewayClient';

export class PaymentService {
  constructor(private readonly gateway: GatewayClient) {}

  async charge(userId: string, amount: number): Promise<{ transactionId: string }> {
    this.validateAmount(amount);
    const result = await this.gateway.processCharge({ userId, amount });
    return { transactionId: result.id };
  }

  validateAmount(amount: number): void {
    if (amount <= 0) {
      throw new Error('Amount must be greater than zero');
    }
  }
}

src/gateway/gatewayClient.ts

export interface ChargeRequest {
  userId: string;
  amount: number;
}

export interface ChargeResult {
  id: string;
  status: string;
}

export class GatewayClient {
  async processCharge(req: ChargeRequest): Promise<ChargeResult> {
    throw new Error('Not implemented');
  }
}

src/billing/__tests__/paymentService.test.ts (starter file — extend this)

import { PaymentService } from '../paymentService';
import { GatewayClient } from '../../gateway/gatewayClient';

jest.mock('../../gateway/gatewayClient');

const mockProcessCharge = GatewayClient.prototype.processCharge as jest.MockedFunction<
  typeof GatewayClient.prototype.processCharge
>;

describe('PaymentService', () => {
  let service: PaymentService;
  let gateway: GatewayClient;

  beforeEach(() => {
    jest.clearAllMocks();
    gateway = new GatewayClient();
    service = new PaymentService(gateway);
  });

  describe('charge', () => {
    it('returns a transaction id on success', async () => {
      mockProcessCharge.mockResolvedValueOnce({ id: 'txn_001', status: 'success' });
      const result = await service.charge('user_1', 50);
      expect(result.transactionId).toBe('txn_001');
    });
  });
});

evals

tile.json