Skip to main content
Crave provides tools for testing your storefront integration without affecting live data. This guide covers test API keys, mock data patterns, and example test suites.

Test mode

Use a test API key (prefixed with sk_test_) to make requests against the production API without creating real orders or processing real payments.
NEXT_PUBLIC_CRAVEUP_API_KEY=sk_test_your_test_key
Test mode behaves identically to production with these differences:
BehaviorProductionTest mode
OrdersSent to restaurantLogged but not sent
PaymentsReal chargesStripe test mode
WebhooksProduction URLTest URL (if configured)
Rate limits200/10 min200/10 min (same)
Generate a test API key from the Dashboard under Settings > API Keys > Test Keys.

Test Stripe payments

In test mode, Crave automatically uses Stripe’s test environment. Use Stripe’s test card numbers:
CardScenario
4242 4242 4242 4242Successful payment
4000 0000 0000 32203D Secure required
4000 0000 0000 0002Card declined
4000 0025 0000 3155SCA authentication required
Use any future expiry date and any 3-digit CVC.

Unit testing the SDK

The SDK accepts a custom fetch implementation, making it easy to mock API responses in tests:
import { describe, it, expect, vi } from 'vitest';
import { createStorefrontClient } from '@craveup/storefront-sdk';

function createMockClient(responses: Record<string, unknown>) {
  const mockFetch = vi.fn(async (url: string) => ({
    ok: true,
    status: 200,
    headers: new Headers({ 'content-length': '1' }),
    json: async () => responses[url] ?? {},
    text: async () => JSON.stringify(responses[url] ?? {}),
  }));

  const client = createStorefrontClient({
    apiKey: 'sk_test_mock',
    fetch: mockFetch as unknown as typeof fetch,
  });

  return { client, mockFetch };
}

describe('cart', () => {
  it('adds an item and returns updated cart', async () => {
    const { client } = createMockClient({
      'https://api.craveup.com/api/v1/locations/loc_123/carts/cart_456/cart-item': {
        cartId: 'cart_456',
        cart: {
          id: 'cart_456',
          totalQuantity: 1,
          orderTotalWithServiceFee: '12.99',
        },
      },
    });

    const result = await client.cart.addItem('loc_123', 'cart_456', {
      productId: 'prod_margherita',
      quantity: 1,
      selections: [],
      itemUnavailableAction: 'remove_item',
    });

    expect(result.cart.totalQuantity).toBe(1);
    expect(result.cart.orderTotalWithServiceFee).toBe('12.99');
  });
});

Test error handling

Verify your app handles API errors correctly:
import { describe, it, expect } from 'vitest';
import { createStorefrontClient, ApiError } from '@craveup/storefront-sdk';

describe('error handling', () => {
  it('throws ApiError on 400 response', async () => {
    const mockFetch = async () => ({
      ok: false,
      status: 400,
      statusText: 'Bad Request',
      headers: new Headers(),
      text: async () => JSON.stringify({
        error: 'VALIDATION_ERROR',
        message: 'Invalid product ID',
      }),
    });

    const client = createStorefrontClient({
      apiKey: 'sk_test_mock',
      fetch: mockFetch as unknown as typeof fetch,
    });

    await expect(
      client.cart.addItem('loc_123', 'cart_456', {
        productId: 'invalid',
        quantity: 1,
        selections: [],
        itemUnavailableAction: 'remove_item',
      })
    ).rejects.toThrow(ApiError);
  });
});

Integration testing

For integration tests against the real API, use your test API key and clean up after each test:
import { describe, it, expect, afterEach } from 'vitest';
import { createStorefrontClient } from '@craveup/storefront-sdk';

const storefront = createStorefrontClient({
  apiKey: process.env.CRAVEUP_TEST_API_KEY!,
});

const LOCATION_ID = process.env.CRAVEUP_TEST_LOCATION_ID!;
let cartId: string | undefined;

afterEach(async () => {
  if (cartId) {
    await storefront.cart.delete(LOCATION_ID, cartId).catch(() => {});
    cartId = undefined;
  }
});

describe('ordering flow', () => {
  it('creates a session and adds an item', async () => {
    const session = await storefront.orderingSessions.start(LOCATION_ID, {
      marketplaceId: LOCATION_ID,
    });

    expect(session.cartId).toBeDefined();
    cartId = session.cartId!;

    const result = await storefront.cart.addItem(LOCATION_ID, cartId, {
      productId: 'prod_test_item',
      quantity: 1,
      selections: [],
      itemUnavailableAction: 'remove_item',
    });

    expect(result.cart.totalQuantity).toBe(1);
  });
});

Testing webhook handlers

Send mock webhook payloads to your local handler:
import { describe, it, expect } from 'vitest';
import { createHmac } from 'crypto';

const WEBHOOK_SECRET = 'whsec_test_secret';

function signPayload(payload: string): string {
  return createHmac('sha256', WEBHOOK_SECRET).update(payload).digest('hex');
}

describe('webhook handler', () => {
  it('processes order.created events', async () => {
    const payload = JSON.stringify({
      id: 'evt_test_123',
      type: 'order.created',
      data: {
        orderId: 'ord_test_456',
        cartId: 'cart_test_789',
        locationId: 'loc_test_123',
        customerName: 'Test User',
        total: '25.98',
      },
    });

    const response = await fetch('http://localhost:3000/api/webhooks', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Crave-Signature': signPayload(payload),
      },
      body: payload,
    });

    expect(response.status).toBe(200);
  });
});

Next steps