Files
opencode-skill/skills/testing-patterns/SKILL.md
Kunthawat Greethong 7edf5bc4d0 feat: Import 35+ skills, merge duplicates, add openclaw installer
Major updates:
- Added 35+ new skills from awesome-opencode-skills and antigravity repos
- Merged SEO skills into seo-master
- Merged architecture skills into architecture
- Merged security skills into security-auditor and security-coder
- Merged testing skills into testing-master and testing-patterns
- Merged pentesting skills into pentesting
- Renamed website-creator to thai-frontend-dev
- Replaced skill-creator with github version
- Removed Chutes references (use MiniMax API instead)
- Added install-openclaw-skills.sh for cross-platform installation
- Updated .env.example with MiniMax API credentials
2026-03-26 11:37:39 +07:00

7.6 KiB

name, description
name description
testing-patterns Testing patterns for JavaScript/TypeScript and Python. pytest, Jest, mocking, fixtures, TDD patterns. Use when writing tests or setting up test infrastructure.

Testing Patterns

Comprehensive testing patterns for JavaScript/TypeScript and Python.


Quick Reference

Language Framework Use Section
JavaScript/TypeScript Jest, Vitest JavaScript Testing
Python pytest Python Testing
Shell bats Shell Testing

JavaScript/TypeScript Testing

Frameworks

  • Jest - Most popular, built-in mocking
  • Vitest - Vite-native, fast, compatible with Jest
  • Mocha - Flexible, requires external assertions

Jest Setup

// jest.config.js
module.exports = {
  testEnvironment: 'node',
  testMatch: ['**/__tests__/**/*.test.js'],
  collectCoverage: true,
  coveragePathIgnorePatterns: ['/node_modules/'],
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
};

Basic Test Structure

describe('Calculator', () => {
  let calculator;

  beforeEach(() => {
    calculator = new Calculator();
  });

  it('should add two numbers', () => {
    expect(calculator.add(2, 3)).toBe(5);
  });

  it('should throw for invalid input', () => {
    expect(() => calculator.add('a', 3)).toThrow(TypeError);
  });
});

Mocking

// Mock a function
const mockFn = jest.fn();
mockFn.mockReturnValue(42);

// Mock a module
jest.mock('./api', () => ({
  fetchUser: jest.fn().mockResolvedValue({ id: 1, name: 'John' }),
}));

// Spy on a method
const spy = jest.spyOn(calculator, 'add');
calculator.add(2, 3);
expect(spy).toHaveBeenCalledWith(2, 3);

Async Testing

it('should fetch user', async () => {
  const user = await fetchUser(1);
  expect(user.name).toBe('John');
});

it('should handle error', async () => {
  await expect(fetchUser(-1)).rejects.toThrow('Not found');
});

React Component Testing

import { render, screen, fireEvent } from '@testing-library/react';

test('should render login form', () => {
  render(<LoginForm />);
  expect(screen.getByLabelText('Email')).toBeInTheDocument();
  fireEvent.change(screen.getByLabelText('Email'), {
    target: { value: 'test@example.com' },
  });
});

Vitest (Modern Alternative)

import { describe, it, expect, beforeEach } from 'vitest';
import { mount } from '@vue/test-utils';

describe('Counter', () => {
  it('increments', async () => {
    const wrapper = mount(Counter);
    await wrapper.find('button').trigger('click');
    expect(wrapper.text()).toContain('1');
  });
});

Python Testing (pytest)

pytest Setup

pip install pytest pytest-cov pytest-mock pytest-asyncio

Basic Test Structure

import pytest

class TestCalculator:
    @pytest.fixture
    def calculator(self):
        return Calculator()
    
    def test_add(self, calculator):
        assert calculator.add(2, 3) == 5
    
    def test_invalid_input(self, calculator):
        with pytest.raises(TypeError):
            calculator.add("a", 3)

Fixtures

@pytest.fixture
def user():
    return User(name="John", email="john@example.com")

@pytest.fixture
def db_session():
    session = create_session()
    yield session
    session.close()

def test_user(db_session, user):
    db_session.add(user)
    db_session.commit()
    assert db_session.query(User).count() == 1

Mocking

from unittest.mock import Mock, patch

@patch('api.fetch_user')
def test_fetch_user(mock_fetch):
    mock_fetch.return_value = {"id": 1, "name": "John"}
    
    result = get_user(1)
    assert result["name"] == "John"
    mock_fetch.assert_called_once_with(1)

# Async mocking
@pytest.mark.asyncio
@patch('api.async_fetch_user')
async def test_async_fetch(mock_fetch):
    mock_fetch.return_value = {"id": 1}
    result = await get_user(1)
    assert result["id"] == 1

Parameterized Tests

@pytest.mark.parametrize("input,expected", [
    (2, 4),
    (3, 9),
    (4, 16),
])
def test_square(input, expected):
    assert square(input) == expected

Testing Exceptions

def test_raises():
    with pytest.raises(ValueError, match="must be positive"):
        factorial(-1)

Database Testing

@pytest.fixture
def test_db():
    engine = create_test_engine()
    Base.metadata.create_all(engine)
    yield session
    Base.metadata.drop_all(engine)

def test_user_crud(test_db):
    user = User(name="John")
    test_db.add(user)
    test_db.commit()
    
    retrieved = test_db.query(User).first()
    assert retrieved.name == "John"

Async Testing

pytestmark = pytest.mark.asyncio

async def test_async_fetch():
    result = await fetch_data()
    assert result is not None

Shell Testing (BATS)

BATS Setup

bats my_tests.bats

Basic Test Structure

#!/usr/bin/env bats

@test "addition" {
  result=$(echo "2 + 3" | bc)
  [ "$result" -eq 5 ]
}

@test "file exists" {
  touch /tmp/testfile
  [ -f /tmp/testfile ]
}

@test "command succeeds" {
  run ls /tmp
  [ "$status" -eq 0 ]
}

Setup/Teardown

setup() {
  echo "Before each test"
}

teardown() {
  echo "After each test"
}

Testing Best Practices

Test Naming

// Good
describe('UserService', () => {
  it('should return null for non-existent user');
  it('should hash password before storing');
  it('should throw ValidationError for invalid email');
});

// Bad
it('test1');
it('user test');

AAA Pattern

// Arrange - Set up test data
const user = { name: 'John', email: 'john@example.com' };

// Act - Execute the code
const result = userService.create(user);

// Assert - Verify the outcome
expect(result.id).toBeDefined();
expect(result.password).not.toBe('plaintext');

Test Isolation

// Bad - shared state
let user;
it('creates', () => { user = create(); });
it('modifies', () => { modify(user.id); }); // depends on first test

// Good - isolated
it('creates and modifies', () => {
  const user = create();
  modify(user.id);
});

Meaningful Assertions

// Bad
expect(result).toBeDefined();

// Good
expect(result).toEqual({
  id: '123',
  name: 'John',
  email: 'john@example.com'
});

Fast Tests

  • Mock external APIs
  • Use in-memory databases
  • Parallel test execution
  • Avoid sleep() - use explicit waits

Mocking Patterns

HTTP Requests (JavaScript)

// Mock fetch
global.fetch = jest.fn().mockResolvedValue({
  ok: true,
  json: () => Promise.resolve({ data: 'test' }),
});

// Mock axios
jest.mock('axios');
axios.get.mockResolvedValue({ data: { id: 1 } });

HTTP Requests (Python)

import requests_mock
from requests_mock import DELETE, GET, POST

def test_api(requests_mock):
    requests_mock.register_uri(GET, 'https://api.example.com/user',
                               json={'id': 1, 'name': 'John'})
    
    response = requests.get('https://api.example.com/user')
    assert response.json()['name'] == 'John'

Time/Mocking

// Jest
jest.useFakeTimers();
jest.setSystemTime(new Date('2024-01-01'));
// ... test code
jest.useRealTimers();
# Python
from freezegun import freeze_time

@freeze_time("2024-01-01")
def test_time_based():
    assert get_current_year() == 2024

Coverage

Jest Coverage

// jest.config.js
module.exports = {
  collectCoverage: true,
  coverageThreshold: {
    global: {
      branches: 70,
      functions: 70,
      lines: 70,
      statements: 70,
    },
  },
};

pytest Coverage

pytest --cov=src --cov-report=html --cov-fail-under=70