home / skills / toilahuongg / shopify-agents-kit / shopify-testing

shopify-testing skill

/.claude/skills/shopify-testing

This skill guides you through unit, integration, and E2E testing for Shopify apps using Remix, Vitest, and Playwright.

npx playbooks add skill toilahuongg/shopify-agents-kit --skill shopify-testing

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
3.5 KB
---
name: shopify-testing
description: Guide for testing Shopify Apps, including Unit Testing with Remix, Mocking Shopify Context, and E2E Testing.
---

# Shopify App Testing

Reliable testing is crucial for ensuring your app handles Shopify's authentication and API quirks correctly.

## 1. Unit & Integration Testing (Vitest + Remix)

Use **Vitest** for running unit and integration tests in the Remix environment.

### Setup
Install dependencies:
```bash
npm install -D vitest @testing-library/react @testing-library/jest-dom jsdom
```

### Mocking `shopify.server.ts`
Creating a mock for the `authenticate` object is critical for testing loaders and actions without hitting real Shopify APIs.

```typescript
// test/mocks/shopify.ts
import { vi } from 'vitest';

export const mockShopify = {
  authenticate: {
    admin: vi.fn(),
    public: vi.fn(),
    webhook: vi.fn(),
  },
};

// Usage in test file:
vi.mock('../app/shopify.server', () => mockShopify);

test('loader returns data for authenticated shop', async () => {
    mockShopify.authenticate.admin.mockResolvedValue({
        currentShop: 'test-shop.myshopify.com',
        session: { shop: 'test-shop.myshopify.com', accessToken: 'fake_token' },
        admin: {
             graphql: vi.fn().mockResolvedValue({ /* mock response */ })
        }
    });

    const response = await loader({ request: new Request('http://localhost/') });
    // Assertions...
});
```

## 2. Testing Loaders & Actions

Using Remix's `createRemixStub` or calling loaders/actions directly is the best way to test backend logic.

### Direct Function Call (Preferred for logic)
You can import the `loader` or `action` and call it directly with a mock Request.

```typescript
import { loader } from '../app/routes/app.dashboard';

test('dashboard loader returns stats', async () => {
   // Setup mocks...
   const response = await loader({ 
       request: new Request('http://localhost/app/dashboard'), 
       params: {} 
   });
   const data = await response.json();
   expect(data.stats).toBeDefined();
});
```

## 3. End-to-End (E2E) Testing (Playwright)

For E2E tests, you need to handle the OAuth flow or bypass it using session tokens.

### Bypassing Auth (Session Token)
The most stable way to E2E test embedded apps is to generate a valid session token (or mock the validation) so you don't have to automate the Login screen interaction which often triggers captchas.

### Basic Playwright Test
```typescript
import { test, expect } from '@playwright/test';

test('load dashboard', async ({ page }) => {
  await page.goto('http://localhost:3000/app');
  // Expect to see the Polaris Page title
  await expect(page.locator('.Polaris-Page-Header__Title')).toHaveText('Dashboard');
});

```

## 4. Testing Webhooks

To test webhooks locally:

1.  **Trigger via Shopify CLI**: `shopify app webhook trigger --topic ORDERS_CREATE`
2.  **Unit Test**: Import the webhook action and pass a mock Request with the correct HMAC header.

```typescript
import { action } from '../app/routes/webhooks';
import crypto from 'crypto';

test('webhook verifies hmac', async () => {
    const payload = JSON.stringify({ id: 123 });
    const hmac = crypto.createHmac('sha256', 'FULL_SECRET').update(payload).digest('base64');
    
    const request = new Request('http://localhost/webhooks', {
        method: 'POST',
        body: payload,
        headers: { 'X-Shopify-Hmac-Sha256': hmac }
    });

    // Mock authenticate.webhook to succeed
    // ...
    
    const response = await action({ request });
    expect(response.status).toBe(200);
});
```

Overview

This skill is a practical guide for testing Shopify Apps with focus on unit, integration, and end-to-end testing. It covers mocking Shopify context in Remix, running tests with Vitest, E2E flows with Playwright, and webhook verification strategies.

How this skill works

It shows how to replace Shopify server helpers with lightweight mocks so loaders and actions can be called directly in tests. It explains setting up Vitest and testing UI and backend logic, how to bypass or simulate OAuth/session tokens for Playwright E2E tests, and how to create and verify webhook requests locally.

When to use it

  • When you need fast unit and integration tests for Remix loaders and actions without hitting Shopify APIs
  • When writing E2E tests that must reliably load an embedded Shopify app UI
  • When validating webhook handling and HMAC verification locally
  • When you want deterministic tests by mocking Shopify authentication and GraphQL responses
  • When setting up CI that runs both unit and E2E suites for a Shopify app

Best practices

  • Mock the shopify.server authenticate object (admin, public, webhook) to avoid network calls
  • Call loaders/actions directly with a mock Request for deterministic backend tests
  • Use Vitest with jsdom and testing-library for fast unit and component tests
  • Generate or mock valid session tokens for Playwright to bypass OAuth and avoid captchas
  • Verify webhook HMACs in tests by constructing payloads and computing expected HMACs

Example use cases

  • Unit test a dashboard loader by mocking authenticate.admin and asserting returned JSON
  • Integration test a form action by calling the action with a crafted Request and form data
  • Playwright test that navigates to /app and checks Polaris page title after injecting a session token
  • Local webhook test that builds a signed payload and calls the webhook action to assert status 200
  • CI job that runs Vitest suite and a headless Playwright smoke test using mocked auth

FAQ

Do I need to run Shopify APIs for tests?

No — mock the authenticate object and GraphQL responses to keep tests fast and reliable.

How do I avoid OAuth in E2E tests?

Generate or mock a valid session token and inject it into the app context or bypass validation in test mode.