McTaba Labs logo
Software EngineeringThe WhiteboardProjectsEventsMasterclassesWebinarsLearn
Book Call

500+ developers already in the community

Get answers, find accountability partners, and stay ahead

Join Discord →
McTaba Labs logo

Educating 1 million students in tech by 2030.

Programs

  • HighSchool Graduates Launchpad
  • AI & Business Automation
  • Corporate Training
  • McTaba Story & Mission
  • Full-Stack Curriculum
  • Course Pricing & Plans
  • Frequently Asked Questions

Resources

  • Join Discord Community
  • Tech Events in Nairobi
  • Masterclasses
  • Free Resources
  • Learn
  • Campus
  • Africa Tech Talent Report
  • Partner With Us
  • Start a Project
  • Our Work

Contact Us

  • 124 Manyani East Road, Lavington, Nairobi
  • +254727795603
  • Book a Discovery Call

Office Hours:

Mon-Fri: 9am - 2pm EAT

Sat: 10am - 12pm EAT

© 2027 McTaba Labs - A product of Edtech Industries. All rights reserved.

Privacy PolicyTerms of ServiceRefund Policy
  1. Learn
  2. Paystack for African Developers
  3. Mocking the Paystack API in Jest
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Mocking the Paystack API in Jest

Mock the Paystack API in Jest by using jest.mock() on your Paystack wrapper module, or by intercepting fetch calls with jest.spyOn(global, "fetch"). Return mock resolved values for success cases and rejected promises for error cases. This lets you test your handler logic for every scenario without network calls.

In this article

  1. 1. Mocking a Paystack Wrapper Module
  2. 2. Mocking fetch Directly
  3. 3. Learn More

Mocking a Paystack Wrapper Module

// lib/paystack.js — your wrapper
const fetch = require('node-fetch');

async function initializeTransaction({ email, amount, reference }) {
  var res = await fetch('https://api.paystack.co/transaction/initialize', {
    method: 'POST',
    headers: { Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY },
    body: JSON.stringify({ email, amount, reference }),
  });
  return res.json();
}

async function verifyTransaction(reference) {
  var res = await fetch('https://api.paystack.co/transaction/verify/' + reference, {
    headers: { Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY },
  });
  return res.json();
}

module.exports = { initializeTransaction, verifyTransaction };

// --- payment.test.js ---
jest.mock('./lib/paystack', () => ({
  initializeTransaction: jest.fn(),
  verifyTransaction: jest.fn(),
}));

const paystack = require('./lib/paystack');
const { createCheckout } = require('./payment-service');

describe('createCheckout', () => {
  beforeEach(() => jest.clearAllMocks());

  it('returns authorization_url on success', async () => {
    paystack.initializeTransaction.mockResolvedValue({
      status: true,
      data: {
        authorization_url: 'https://checkout.paystack.com/abc',
        reference: 'ref_001',
      },
    });

    var result = await createCheckout({ email: 'user@test.com', amount: 100000 });
    expect(result.authorization_url).toBe('https://checkout.paystack.com/abc');
  });

  it('throws on Paystack API failure', async () => {
    paystack.initializeTransaction.mockRejectedValue(new Error('Network error'));
    await expect(createCheckout({ email: 'user@test.com', amount: 100000 })).rejects.toThrow('Network error');
  });
});

Mocking fetch Directly

// If your code calls fetch directly without a wrapper
global.fetch = jest.fn();

describe('verifyPayment', () => {
  it('returns success data when transaction is paid', async () => {
    global.fetch.mockResolvedValueOnce({
      json: () => Promise.resolve({
        status: true,
        data: { status: 'success', amount: 100000, reference: 'ref_001' },
      }),
    });

    var result = await verifyPayment('ref_001');
    expect(result.data.status).toBe('success');
  });

  it('handles 5xx server error from Paystack', async () => {
    global.fetch.mockResolvedValueOnce({
      ok: false,
      status: 500,
      json: () => Promise.resolve({ status: false, message: 'Server error' }),
    });

    await expect(verifyPayment('ref_bad')).rejects.toThrow();
  });

  afterEach(() => jest.resetAllMocks());
});

Get weekly developer tips

Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.

No spam. Unsubscribe anytime.

Learn More

This guide is part of the Paystack testing guide.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Use jest.mock() to replace your Paystack wrapper module — all imports of the module in the code under test get the mock.
  • ✓For code that calls fetch directly, use jest.spyOn(global, "fetch").mockResolvedValue() to intercept HTTP calls.
  • ✓Mock different responses for different test cases: success, decline, network error, 5xx server error.
  • ✓Reset mocks between tests with jest.clearAllMocks() or afterEach(() => jest.restoreAllMocks()).
  • ✓Test the charge.success, charge.failed, and error scenarios in separate test cases — do not combine them.
  • ✓Avoid nock or other HTTP interceptors when a simple jest.mock on your wrapper module is sufficient.

Frequently Asked Questions

Should I use nock or msw to mock Paystack API calls?
For unit tests of a Paystack wrapper module, jest.mock() is the simplest approach — no extra libraries needed. For integration tests where you want to intercept actual HTTP calls from multiple modules, msw (Mock Service Worker) is a clean option. Nock works for Node.js but requires careful cleanup between tests.
How do I test that my code retries on a 429 rate limit response?
Mock the first call to return a 429 response and the second to return success. Then call your retry-aware function and assert it called the mocked fetch twice. Example: global.fetch.mockResolvedValueOnce({ status: 429, json: () => ({ message: "Rate limit" }) }).mockResolvedValueOnce({ status: 200, json: () => ({ status: true, data: {} }) });
Do I need to set PAYSTACK_SECRET_KEY in Jest tests?
When you mock the Paystack wrapper module with jest.mock(), the actual API call never happens, so no real key is needed. If you test code that reads process.env.PAYSTACK_SECRET_KEY directly (e.g., to construct headers), set it in your jest.config.js or in the test file: process.env.PAYSTACK_SECRET_KEY = "sk_test_fake";
What is the difference between mockResolvedValue and mockResolvedValueOnce?
mockResolvedValue sets the return value for all future calls to that mock. mockResolvedValueOnce sets it for only the next call — subsequent calls use the default return value or the next queued value. Use mockResolvedValueOnce when testing sequences (e.g., first call returns error, second returns success for retry testing).

Learn Paystack Integration

KES 2,999

The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.

Start Paystack Course

Not ready? Create Free Account

Keep reading

Testing Paystack Integrations: The Complete Guide

A complete guide to testing Paystack integrations at every level: test mode, test cards, unit tests with mocks, integration tests, end-to-end flows with Playwright, webhook simulation, contract testing, CI pipelines, staging environments, load testing, and coverage targets for payment code.

Read more

Simulating Declined Cards on Paystack

A card decline is one of the most common failure states in a payment flow, and your application must handle it gracefully. This guide shows how to simulate specific card declines in Paystack test mode and how to verify your decline handling code works correctly.

Read more

Integration Testing Paystack Flows End to End

Integration tests for Paystack test the real flow — your backend calls the actual Paystack test API, a webhook fires to your server, and your database is updated. Unlike unit tests with mocks, integration tests catch issues that only appear when the real API is involved.

Read more

Seeding Test Data for a Payments Feature

Good test data is essential for testing payment features. This guide covers creating Paystack test mode resources (plans, subaccounts, split codes) and seeding your application database with matching payment records for realistic test scenarios.

Read more

Transaction Not Found on Paystack Verify

Paystack returns "Transaction not found" when the reference you pass to the verify endpoint does not match any transaction in the current environment. The causes range from wrong reference strings to test/live key confusion to URL encoding problems. This guide covers every cause with specific fixes.

Read more

Explore related topics

M-Pesa & African Stack

M-Pesa Daraja API tutorials, USSD development, and WhatsApp Business API guides. The African Stack that global bootcamps do not teach.

Learn to Code in Kenya

Everything a Kenyan needs to know before writing their first line of code. Career paths, real costs, hardware requirements, and honest answers about what it takes.

Learning to Code

Roadmaps, language guides, and practical advice for learning to code and launching a programming career.

Breaking Into Tech

Practical, no-nonsense guides for anyone looking to start or change their career in tech. From choosing your first language to acing interviews.

The African Stack: Skills for This Market

The skills that make Kenyan developers hard to replace: M-Pesa, USSD, WhatsApp, fintech, and building for the African market.

Bonaventure Ogeto

Bonaventure Ogeto

Founder, McTaba Labs

Bonaventure is the founder of McTaba Labs, a coding bootcamp in Nairobi, Kenya. He has spent years building products for the African market and mentoring the next generation of developers across the continent.