Files
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

405 lines
7.6 KiB
Markdown

---
name: testing-patterns
description: |
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
```javascript
// jest.config.js
module.exports = {
testEnvironment: 'node',
testMatch: ['**/__tests__/**/*.test.js'],
collectCoverage: true,
coveragePathIgnorePatterns: ['/node_modules/'],
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
};
```
### Basic Test Structure
```javascript
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
```javascript
// 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
```javascript
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
```javascript
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)
```javascript
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
```bash
pip install pytest pytest-cov pytest-mock pytest-asyncio
```
### Basic Test Structure
```python
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
```python
@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
```python
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
```python
@pytest.mark.parametrize("input,expected", [
(2, 4),
(3, 9),
(4, 16),
])
def test_square(input, expected):
assert square(input) == expected
```
### Testing Exceptions
```python
def test_raises():
with pytest.raises(ValueError, match="must be positive"):
factorial(-1)
```
### Database Testing
```python
@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
```python
pytestmark = pytest.mark.asyncio
async def test_async_fetch():
result = await fetch_data()
assert result is not None
```
---
## Shell Testing (BATS)
### BATS Setup
```bash
bats my_tests.bats
```
### Basic Test Structure
```bash
#!/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
```bash
setup() {
echo "Before each test"
}
teardown() {
echo "After each test"
}
```
---
## Testing Best Practices
### Test Naming
```javascript
// 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
```javascript
// 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
```javascript
// 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
```javascript
// 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)
```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)
```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
```javascript
// Jest
jest.useFakeTimers();
jest.setSystemTime(new Date('2024-01-01'));
// ... test code
jest.useRealTimers();
```
```python
# Python
from freezegun import freeze_time
@freeze_time("2024-01-01")
def test_time_based():
assert get_current_year() == 2024
```
---
## Coverage
### Jest Coverage
```javascript
// jest.config.js
module.exports = {
collectCoverage: true,
coverageThreshold: {
global: {
branches: 70,
functions: 70,
lines: 70,
statements: 70,
},
},
};
```
### pytest Coverage
```bash
pytest --cov=src --cov-report=html --cov-fail-under=70
```