From backend-nodejs
Use when writing tests for Node.js and TypeScript code — Jest or Vitest with supertest for HTTP testing, proper mocking patterns, and async test conventions
How this skill is triggered — by the user, by Claude, or both
Slash command
/backend-nodejs:nodejs-testingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
```typescript
describe("OrderService", () => {
let service: OrderService;
let mockRepo: jest.Mocked<OrderRepository>;
beforeEach(() => {
mockRepo = {
save: jest.fn(),
findById: jest.fn(),
} as any;
service = new OrderService(mockRepo);
});
it("should create order with valid input", async () => {
// Arrange
const input: CreateOrderInput = {
customerId: "c1",
items: [{ productId: "p1", quantity: 2 }],
};
mockRepo.save.mockResolvedValue({ id: 1, ...input });
// Act
const result = await service.create(input);
// Assert
expect(result.id).toBe(1);
expect(mockRepo.save).toHaveBeenCalledTimes(1);
});
it("should throw ValidationError when items is empty", async () => {
const input = { customerId: "c1", items: [] };
await expect(service.create(input)).rejects.toThrow(ValidationError);
});
});
describe("POST /api/v1/orders", () => {
it("should return 201 for valid order", async () => {
const response = await request(app)
.post("/api/v1/orders")
.send({ customerId: "c1", items: [{ productId: "p1", quantity: 1 }] })
.expect(201);
expect(response.body.id).toBeDefined();
});
it("should return 400 for empty items", async () => {
await request(app)
.post("/api/v1/orders")
.send({ customerId: "c1", items: [] })
.expect(400);
});
});
jest.fn() / vi.fn() for function mocksjest.mock() / vi.mock() for module mocksorder-service.test.ts next to order-service.ts__tests__/integration/ directory__fixtures__/ for test dataStructure each describe block to include positive, negative, edge, and security scenarios.
describe("OrderService", () => {
it("should create order with valid input", async () => {
const input = { customerId: "c1", items: [{ productId: "p1", quantity: 2 }] };
mockRepo.save.mockResolvedValue({ id: 1, ...input });
const result = await service.create(input);
expect(result.id).toBe(1);
expect(mockRepo.save).toHaveBeenCalledTimes(1);
});
});
describe("OrderService", () => {
it("should throw ValidationError when items is empty", async () => {
const input = { customerId: "c1", items: [] };
await expect(service.create(input)).rejects.toThrow(ValidationError);
});
it("should throw NotFoundError when order does not exist", async () => {
mockRepo.findById.mockResolvedValue(null);
await expect(service.getById(999)).rejects.toThrow(NotFoundError);
});
});
describe("OrderService", () => {
it("should handle max-length customer ID (255 chars)", async () => {
const input = { customerId: "c".repeat(255), items: [{ productId: "p1", quantity: 1 }] };
mockRepo.save.mockResolvedValue({ id: 1, ...input });
const result = await service.create(input);
expect(result.customerId).toHaveLength(255);
});
it("should reject zero quantity", async () => {
const input = { customerId: "c1", items: [{ productId: "p1", quantity: 0 }] };
await expect(service.create(input)).rejects.toThrow(ValidationError);
});
it("should handle concurrent duplicate requests idempotently", async () => {
const input = { customerId: "c1", items: [{ productId: "p1", quantity: 1 }], idempotencyKey: "key-1" };
mockRepo.save.mockResolvedValue({ id: 1, ...input });
const [r1, r2] = await Promise.all([service.create(input), service.create(input)]);
expect(r1.id).toBe(r2.id);
});
});
describe("OrderService — security", () => {
it.each([
["SQL injection", "'; DROP TABLE orders; --"],
["XSS payload", "<script>alert(1)</script>"],
["path traversal", "../../../etc/passwd"],
])("should reject %s in customer ID", async (_label, maliciousInput) => {
const input = { customerId: maliciousInput, items: [{ productId: "p1", quantity: 1 }] };
await expect(service.create(input)).rejects.toThrow(ValidationError);
});
});
describe("POST /api/v1/orders — security", () => {
it("should return 401 without auth token", async () => {
const response = await request(app)
.post("/api/v1/orders")
.send({ customerId: "c1", items: [{ productId: "p1", quantity: 1 }] });
expect(response.status).toBe(401);
expect(response.body).not.toHaveProperty("stack");
expect(JSON.stringify(response.body)).not.toContain("connection");
});
it("should ignore mass-assignment fields", async () => {
const response = await request(app)
.post("/api/v1/orders")
.set("Authorization", `Bearer ${validToken}`)
.send({ customerId: "c1", items: [{ productId: "p1", quantity: 1 }], isAdmin: true, discount: 100 });
expect(response.status).toBe(201);
expect(response.body.discount).toBeUndefined();
});
});
npx claudepluginhub gagandeepp/software-agent-teams --plugin backend-nodejsGuides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Enforces test-driven development: write failing test first, then minimal code to pass. Use when implementing features or bugfixes.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.