xiaohongshu
xiaohongshu
gate-4a-unit-tests
Gate 4a validation: Unit test creation and nock-only enforcement
Full skill instructions
Purpose: Validate unit test quality, mocking strategy, and coverage
STOP AND CHECK:
# 1. Unit test files exist
if ! ls test/unit/*Test.ts 2>/dev/null | head -1 > /dev/null; then
echo "❌ Gate 4a FAILED: No unit test files found"
echo " Create test/unit/{Resource}ProducerTest.ts"
echo " See unit-test-patterns skill"
exit 1
fi
TEST_FILE_COUNT=$(ls test/unit/*Test.ts 2>/dev/null | wc -l)
echo "✅ Found $TEST_FILE_COUNT unit test file(s)"
# 2. Test suites exist with proper structure
DESCRIBE_COUNT=$(grep -h "^describe(" test/unit/*.ts 2>/dev/null | wc -l)
if [ "$DESCRIBE_COUNT" -lt 1 ]; then
echo "❌ Gate 4a FAILED: No describe blocks found"
echo " Unit tests must have describe blocks"
exit 1
fi
echo "✅ Found $DESCRIBE_COUNT describe block(s)"
# 3. Multiple test cases (3+ per operation)
IT_COUNT=$(grep -h "^\s*it(" test/unit/*.ts 2>/dev/null | wc -l)
if [ "$IT_COUNT" -lt 3 ]; then
echo "❌ Gate 4a FAILED: Insufficient test cases (found $IT_COUNT, need 3+)"
echo " Each operation needs multiple test cases including error scenarios"
exit 1
fi
echo "✅ Found $IT_COUNT test case(s)"
# 4. CRITICAL: ONLY nock used for HTTP mocking
if ! grep -h "from ['\"]nock['\"]" test/unit/*.ts 2>/dev/null | head -1 > /dev/null; then
echo "❌ Gate 4a FAILED: nock not imported in unit tests"
echo " Unit tests MUST use nock for HTTP mocking"
echo ""
echo "Add to test files:"
echo " import * as nock from 'nock';"
echo ""
echo "See nock-patterns skill for examples"
exit 1
fi
echo "✅ nock imported for HTTP mocking"
# 5. CRITICAL: No forbidden mocking libraries
FORBIDDEN_MOCKS=$(grep -h -E "jest\.mock|sinon|fetch-mock|msw" test/unit/*.ts 2>/dev/null || true)
if [ -n "$FORBIDDEN_MOCKS" ]; then
echo "❌ Gate 4a FAILED: Forbidden mocking library detected"
echo " ONLY nock is allowed for HTTP mocking"
echo ""
echo "Found forbidden libraries:"
echo "$FORBIDDEN_MOCKS"
echo ""
echo "Fix: Replace with nock HTTP mocking"
echo "See failure-conditions skill Rule #4"
exit 1
fi
echo "✅ No forbidden mocking libraries"
# 6. Verify Common.ts exists and is properly structured
if [ ! -f "test/unit/Common.ts" ]; then
echo "❌ Gate 4a FAILED: test/unit/Common.ts missing"
echo " Create Common.ts with getConnectedInstance helper"
echo " See unit-test-patterns skill for template"
exit 1
fi
# Verify Common.ts uses nock (not real credentials)
if ! grep -q "import.*nock" test/unit/Common.ts; then
echo "⚠️ WARNING: Common.ts should import nock"
echo " Unit test Common.ts must use mocked HTTP, not real API"
fi
# Verify Common.ts does NOT use dotenv or process.env
if grep -E "dotenv|process\.env" test/unit/Common.ts > /dev/null 2>&1; then
echo "❌ Gate 4a FAILED: Common.ts uses environment variables"
echo " Unit tests must NOT depend on .env or process.env"
echo " Use nock to mock HTTP requests instead"
echo ""
echo "Found:"
grep -n -E "dotenv|process\.env" test/unit/Common.ts
exit 1
fi
echo "✅ Common.ts properly structured (no env vars)"
# 7. Verify error cases are tested
ERROR_TESTS=$(grep -h -i "error\|fail\|invalid\|unauthorized\|notfound\|404\|401" test/unit/*.ts 2>/dev/null | wc -l)
if [ "$ERROR_TESTS" -lt 1 ]; then
echo "⚠️ WARNING: No error case tests found"
echo " Tests should cover error scenarios:"
echo " - 401 → InvalidCredentialsError"
echo " - 404 → NoSuchObjectError"
echo " - 429 → RateLimitExceededError"
fi
# 8. Verify mocking at HTTP level (not method level)
METHOD_MOCKS=$(grep -h -E "\.mockReturnValue\(|\.mockResolvedValue\(|\.mockImplementation\(" test/unit/*.ts 2>/dev/null || true)
if [ -n "$METHOD_MOCKS" ]; then
echo "⚠️ WARNING: Method-level mocking detected"
echo " Use HTTP-level mocking with nock instead"
echo ""
echo "Found:"
echo "$METHOD_MOCKS"
echo ""
echo "Fix: Use nock('https://api.example.com').get('/path').reply(200, data);"
fi
# 9. Verify nock cleanup in tests
if ! grep -h "nock.cleanAll()" test/unit/*.ts > /dev/null 2>&1; then
echo "⚠️ WARNING: No nock.cleanAll() found"
echo " Add afterEach(() => nock.cleanAll()) to clean up mocks"
fi
# 10. Check test file naming convention
for file in test/unit/*Test.ts; do
if [ -f "$file" ]; then
basename=$(basename "$file")
if [[ ! "$basename" =~ ^[A-Z][a-zA-Z]*Test\.ts$ ]] && [[ "$basename" != "Common.ts" ]]; then
echo "⚠️ WARNING: Incorrect test file naming: $basename"
echo " Should be: {Resource}ProducerTest.ts or ConnectionTest.ts"
fi
fi
done
echo ""
echo "✅ Gate 4a: Unit Test Creation - PASSED"
PROCEED ONLY IF:
IF FAILED:
// ❌ WRONG - forbidden mocking library
import { jest } from '@jest/globals';
jest.mock('./ServiceClient');
const mockClient = {
getUser: jest.fn().mockResolvedValue({ id: '123' })
};
// ❌ WRONG - sinon stubs
import sinon from 'sinon';
const stub = sinon.stub(client, 'getUser').resolves({ id: '123' });
// ✅ CORRECT - nock HTTP mocking
import * as nock from 'nock';
nock('https://api.service.com')
.get('/users/123')
.reply(200, { id: '123', name: 'John' });
const user = await producer.getUser('123');
Why:
// ❌ WRONG - mocking methods
const mockGetUser = jest.fn().mockResolvedValue({ id: '123' });
client.getUser = mockGetUser;
// ❌ WRONG - mocking class instance
jest.spyOn(client, 'getUser').mockReturnValue(Promise.resolve({ id: '123' }));
// ✅ CORRECT - HTTP level with nock
import * as nock from 'nock';
nock('https://api.service.com')
.get('/users/123')
.reply(200, { id: '123', name: 'John' });
// Call real implementation - nock intercepts HTTP request
const user = await producer.getUser('123');
expect(user.id).to.be.instanceof(UUID);
Why:
// ❌ WRONG - only happy path
describe('UserProducer', () => {
it('should list users', async () => {
nock('https://api.service.com')
.get('/users')
.reply(200, [{ id: '123' }]);
const users = await producer.listUsers();
expect(users).to.have.length(1);
});
});
// ✅ CORRECT - include error cases
describe('UserProducer', () => {
it('should list users successfully', async () => {
nock('https://api.service.com')
.get('/users')
.reply(200, [{ id: '123' }]);
const users = await producer.listUsers();
expect(users).to.have.length(1);
});
it('should throw InvalidCredentialsError on 401', async () => {
nock('https://api.service.com')
.get('/users')
.reply(401, { error: 'Unauthorized' });
try {
await producer.listUsers();
expect.fail('Should have thrown an error');
} catch (error: any) {
expect(error).to.be.instanceOf(InvalidCredentialsError);
}
});
it('should throw NoSuchObjectError on 404', async () => {
nock('https://api.service.com')
.get('/users/999')
.reply(404, { error: 'Not found' });
try {
await producer.getUser('999');
expect.fail('Should have thrown an error');
} catch (error: any) {
expect(error).to.be.instanceOf(NoSuchObjectError);
}
});
it('should throw RateLimitExceededError on 429', async () => {
nock('https://api.service.com')
.get('/users')
.reply(429, { error: 'Rate limit exceeded' });
try {
await producer.listUsers();
expect.fail('Should have thrown an error');
} catch (error: any) {
expect(error).to.be.instanceOf(RateLimitExceededError);
}
});
});
Why:
// ❌ WRONG - no cleanup
describe('UserProducer', () => {
it('should get user', async () => {
nock('https://api.service.com')
.get('/users/123')
.reply(200, { id: '123' });
const user = await producer.getUser('123');
expect(user.id).to.be.instanceof(UUID);
});
});
// ✅ CORRECT - cleanup after each test
describe('UserProducer', () => {
afterEach(() => {
nock.cleanAll();
});
it('should get user', async () => {
nock('https://api.service.com')
.get('/users/123')
.reply(200, { id: '123' });
const user = await producer.getUser('123');
expect(user.id).to.be.instanceof(UUID);
});
it('should get another user', async () => {
nock('https://api.service.com')
.get('/users/456')
.reply(200, { id: '456' });
const user = await producer.getUser('456');
expect(user.id).to.be.instanceof(UUID);
});
});
Why:
// ❌ WRONG - unit test Common.ts with env vars
import { config } from 'dotenv';
import { Email } from '@zerobias-org/types-core-js';
import { newService } from '../../src';
config(); // NO! Unit tests don't use .env
export const SERVICE_EMAIL = process.env.SERVICE_EMAIL || '';
export const SERVICE_PASSWORD = process.env.SERVICE_PASSWORD || '';
export async function getConnectedInstance() {
const connector = newService();
await connector.connect({
email: new Email(SERVICE_EMAIL),
password: SERVICE_PASSWORD,
});
return connector;
}
// ✅ CORRECT - unit test Common.ts with nock
import * as nock from 'nock';
import { Email } from '@zerobias-org/types-core-js';
import { newService } from '../../src';
import type { ServiceConnector } from '../../src';
/**
* Get a connected instance for unit testing.
* Uses mocked HTTP - no real credentials needed.
* Unit tests should NEVER depend on environment variables.
*/
export async function getConnectedInstance(): Promise<ServiceConnector> {
nock('https://api.example.com')
.post('/auth/login')
.reply(200, {
accessToken: 'test-token-123',
expiresAt: '2025-10-02T00:00:00Z',
});
const connector = newService();
await connector.connect({
email: new Email('[email protected]'),
password: 'testpass',
});
return connector;
}
Why:
# Verify unit tests exist
ls -lh test/unit/*Test.ts
# Count test suites and cases
echo "Describe blocks:"
grep -h "^describe(" test/unit/*.ts | wc -l
echo "Test cases:"
grep -h "^\s*it(" test/unit/*.ts | wc -l
echo "Error tests:"
grep -h -i "error\|404\|401\|429" test/unit/*.ts | wc -l
# Check nock imports
echo "Files importing nock:"
grep -l "from ['\"]nock['\"]" test/unit/*.ts
# Check for forbidden libraries
echo "Checking for forbidden mocking libraries:"
grep -n -E "jest\.mock|sinon|fetch-mock|msw" test/unit/*.ts && \
echo "❌ FAIL: Forbidden mocking library found" || \
echo "✅ PASS: Only nock used"
# Check nock cleanup
echo "Files with nock.cleanAll():"
grep -l "nock.cleanAll()" test/unit/*.ts
# Check Common.ts structure
echo "Checking test/unit/Common.ts:"
# Should use nock
grep -q "import.*nock" test/unit/Common.ts && \
echo "✅ Uses nock" || \
echo "❌ Should import nock"
# Should NOT use dotenv or process.env
grep -E "dotenv|process\.env" test/unit/Common.ts && \
echo "❌ FAIL: Uses env vars (forbidden in unit tests)" || \
echo "✅ No env vars"
# Run unit tests (should work without .env file)
npm run test:unit
# Expected: All tests pass, no env file needed
Unit test creation MUST meet ALL criteria:
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience