ASD

Javascript Testing Patterns

Implementa estrategias de testing con Jest, Vitest y Testing Library: tests unitarios, de integración y end-to-end, con mocking, fixtures y TDD/BDD.

Estrellas
38.8k

en todo el repo

Actividad
37

0–100, la ruta de este skill

Actualizado
hace 4 meses

último commit aquí

Commits
0

últimos 90 días

Contexto
3.7k tok

77 tok en reposo

Paquete
2 archivos

27 KB

Instalar

Funciona con cualquier agente que lea SKILL.md

npx -y skills add wshobson/agents --skill javascript-testing-patterns --agent claude-code

Se instala solo en este repositorio.

Este skill makes network requests, reads environment config.

Qué hace

  • Configura Jest y Vitest con coverage, entorno de test y setup files
  • Aplica patrones de testing unitario para funciones puras, clases y código async
  • Implementa mocking de módulos, spies e inyección de dependencias para tests aislados
  • Configura tests de integración con supertest y base de datos, y tests de componentes con Testing Library
  • Genera fixtures de datos de prueba con @faker-js/faker usando factories con overrides

Úsalo cuando

  • Configurar infraestructura de testing para proyectos nuevos
  • Escribir tests unitarios de funciones y clases
  • Crear tests de integración para APIs y servicios
  • Implementar tests end-to-end o flujos TDD/BDD

No lo uses cuando

    Qué lo activa

    Di cualquiera de estas frases y el agente debería cargar este skill.

    • Configúrame Jest con coverage al 80% para este proyecto TypeScript
    • Escribe tests unitarios para esta clase UserService con mocks del repositorio
    • Ayúdame a mockear fetch en un test de Vitest
    • Crea un fixture de usuario con faker para mis tests

    SKILL.md

    En inglés

    JavaScript Testing Patterns

    Comprehensive guide for implementing robust testing strategies in JavaScript/TypeScript applications using modern testing frameworks and best practices.

    When to Use This Skill

    • Setting up test infrastructure for new projects
    • Writing unit tests for functions and classes
    • Creating integration tests for APIs and services
    • Implementing end-to-end tests for user flows
    • Mocking external dependencies and APIs
    • Testing React, Vue, or other frontend components
    • Implementing test-driven development (TDD)
    • Setting up continuous testing in CI/CD pipelines

    Testing Frameworks

    Jest - Full-Featured Testing Framework

    Setup:

    // jest.config.ts
    import type { Config } from "jest";
    
    const config: Config = {
      preset: "ts-jest",
      testEnvironment: "node",
      roots: ["<rootDir>/src"],
      testMatch: ["**/__tests__/**/*.ts", "**/?(*.)+(spec|test).ts"],
      collectCoverageFrom: [
        "src/**/*.ts",
        "!src/**/*.d.ts",
        "!src/**/*.interface.ts",
      ],
      coverageThreshold: {
        global: {
          branches: 80,
          functions: 80,
          lines: 80,
          statements: 80,
        },
      },
      setupFilesAfterEnv: ["<rootDir>/src/test/setup.ts"],
    };
    
    export default config;
    

    Vitest - Fast, Vite-Native Testing

    Setup:

    // vitest.config.ts
    import { defineConfig } from "vitest/config";
    
    export default defineConfig({
      test: {
        globals: true,
        environment: "node",
        coverage: {
          provider: "v8",
          reporter: ["text", "json", "html"],
          exclude: ["**/*.d.ts", "**/*.config.ts", "**/dist/**"],
        },
        setupFiles: ["./src/test/setup.ts"],
      },
    });
    

    Unit Testing Patterns

    Pattern 1: Testing Pure Functions

    // utils/calculator.ts
    export function add(a: number, b: number): number {
      return a + b;
    }
    
    export function divide(a: number, b: number): number {
      if (b === 0) {
        throw new Error("Division by zero");
      }
      return a / b;
    }
    
    // utils/calculator.test.ts
    import { describe, it, expect } from "vitest";
    import { add, divide } from "./calculator";
    
    describe("Calculator", () => {
      describe("add", () => {
        it("should add two positive numbers", () => {
          expect(add(2, 3)).toBe(5);
        });
    
        it("should add negative numbers", () => {
          expect(add(-2, -3)).toBe(-5);
        });
    
        it("should handle zero", () => {
          expect(add(0, 5)).toBe(5);
          expect(add(5, 0)).toBe(5);
        });
      });
    
      describe("divide", () => {
        it("should divide two numbers", () => {
          expect(divide(10, 2)).toBe(5);
        });
    
        it("should handle decimal results", () => {
          expect(divide(5, 2)).toBe(2.5);
        });
    
        it("should throw error when dividing by zero", () => {
          expect(() => divide(10, 0)).toThrow("Division by zero");
        });
      });
    });
    

    Pattern 2: Testing Classes

    // services/user.service.ts
    export class UserService {
      private users: Map<string, User> = new Map();
    
      create(user: User): User {
        if (this.users.has(user.id)) {
          throw new Error("User already exists");
        }
        this.users.set(user.id, user);
        return user;
      }
    
      findById(id: string): User | undefined {
        return this.users.get(id);
      }
    
      update(id: string, updates: Partial<User>): User {
        const user = this.users.get(id);
        if (!user) {
          throw new Error("User not found");
        }
        const updated = { ...user, ...updates };
        this.users.set(id, updated);
        return updated;
      }
    
      delete(id: string): boolean {
        return this.users.delete(id);
      }
    }
    
    // services/user.service.test.ts
    import { describe, it, expect, beforeEach } from "vitest";
    import { UserService } from "./user.service";
    
    describe("UserService", () => {
      let service: UserService;
    
      beforeEach(() => {
        service = new UserService();
      });
    
      describe("create", () => {
        it("should create a new user", () => {
          const user = { id: "1", name: "John", email: "john@example.com" };
          const created = service.create(user);
    
          expect(created).toEqual(user);
          expect(service.findById("1")).toEqual(user);
        });
    
        it("should throw error if user already exists", () => {
          const user = { id: "1", name: "John", email: "john@example.com" };
          service.create(user);
    
          expect(() => service.create(user)).toThrow("User already exists");
        });
      });
    
      describe("update", () => {
        it("should update existing user", () => {
          const user = { id: "1", name: "John", email: "john@example.com" };
          service.create(user);
    
          const updated = service.update("1", { name: "Jane" });
    
          expect(updated.name).toBe("Jane");
          expect(updated.email).toBe("john@example.com");
        });
    
        it("should throw error if user not found", () => {
          expect(() => service.update("999", { name: "Jane" })).toThrow(
            "User not found",
          );
        });
      });
    });
    

    Pattern 3: Testing Async Functions

    // services/api.service.ts
    export class ApiService {
      async fetchUser(id: string): Promise<User> {
        const response = await fetch(`https://api.example.com/users/${id}`);
        if (!response.ok) {
          throw new Error("User not found");
        }
        return response.json();
      }
    
      async createUser(user: CreateUserDTO): Promise<User> {
        const response = await fetch("https://api.example.com/users", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(user),
        });
        return response.json();
      }
    }
    
    // services/api.service.test.ts
    import { describe, it, expect, vi, beforeEach } from "vitest";
    import { ApiService } from "./api.service";
    
    // Mock fetch globally
    global.fetch = vi.fn();
    
    describe("ApiService", () => {
      let service: ApiService;
    
      beforeEach(() => {
        service = new ApiService();
        vi.clearAllMocks();
      });
    
      describe("fetchUser", () => {
        it("should fetch user successfully", async () => {
          const mockUser = { id: "1", name: "John", email: "john@example.com" };
    
          (fetch as any).mockResolvedValueOnce({
            ok: true,
            json: async () => mockUser,
          });
    
          const user = await service.fetchUser("1");
    
          expect(user).toEqual(mockUser);
          expect(fetch).toHaveBeenCalledWith("https://api.example.com/users/1");
        });
    
        it("should throw error if user not found", async () => {
          (fetch as any).mockResolvedValueOnce({
            ok: false,
          });
    
          await expect(service.fetchUser("999")).rejects.toThrow("User not found");
        });
      });
    
      describe("createUser", () => {
        it("should create user successfully", async () => {
          const newUser = { name: "John", email: "john@example.com" };
          const createdUser = { id: "1", ...newUser };
    
          (fetch as any).mockResolvedValueOnce({
            ok: true,
            json: async () => createdUser,
          });
    
          const user = await service.createUser(newUser);
    
          expect(user).toEqual(createdUser);
          expect(fetch).toHaveBeenCalledWith(
            "https://api.example.com/users",
            expect.objectContaining({
              method: "POST",
              body: JSON.stringify(newUser),
            }),
          );
        });
      });
    });
    

    Mocking Patterns

    Pattern 1: Mocking Modules

    // services/email.service.ts
    import nodemailer from "nodemailer";
    
    export class EmailService {
      private transporter = nodemailer.createTransport({
        host: process.env.SMTP_HOST,
        port: 587,
        auth: {
          user: process.env.SMTP_USER,
          pass: process.env.SMTP_PASS,
        },
      });
    
      async sendEmail(to: string, subject: string, html: string) {
        await this.transporter.sendMail({
          from: process.env.EMAIL_FROM,
          to,
          subject,
          html,
        });
      }
    }
    
    // services/email.service.test.ts
    import { describe, it, expect, vi, beforeEach } from "vitest";
    import { EmailService } from "./email.service";
    
    vi.mock("nodemailer", () => ({
      default: {
        createTransport: vi.fn(() => ({
          sendMail: vi.fn().mockResolvedValue({ messageId: "123" }),
        })),
      },
    }));
    
    describe("EmailService", () => {
      let service: EmailService;
    
      beforeEach(() => {
        service = new EmailService();
      });
    
      it("should send email successfully", async () => {
        await service.sendEmail(
          "test@example.com",
          "Test Subject",
          "<p>Test Body</p>",
        );
    
        expect(service["transporter"].sendMail).toHaveBeenCalledWith(
          expect.objectContaining({
            to: "test@example.com",
            subject: "Test Subject",
          }),
        );
      });
    });
    

    Pattern 2: Dependency Injection for Testing

    // services/user.service.ts
    export interface IUserRepository {
      findById(id: string): Promise<User | null>;
      create(user: User): Promise<User>;
    }
    
    export class UserService {
      constructor(private userRepository: IUserRepository) {}
    
      async getUser(id: string): Promise<User> {
        const user = await this.userRepository.findById(id);
        if (!user) {
          throw new Error("User not found");
        }
        return user;
      }
    
      async createUser(userData: CreateUserDTO): Promise<User> {
        // Business logic here
        const user = { id: generateId(), ...userData };
        return this.userRepository.create(user);
      }
    }
    
    // services/user.service.test.ts
    import { describe, it, expect, vi, beforeEach } from "vitest";
    import { UserService, IUserRepository } from "./user.service";
    
    describe("UserService", () => {
      let service: UserService;
      let mockRepository: IUserRepository;
    
      beforeEach(() => {
        mockRepository = {
          findById: vi.fn(),
          create: vi.fn(),
        };
        service = new UserService(mockRepository);
      });
    
      describe("getUser", () => {
        it("should return user if found", async () => {
          const mockUser = { id: "1", name: "John", email: "john@example.com" };
          vi.mocked(mockRepository.findById).mockResolvedValue(mockUser);
    
          const user = await service.getUser("1");
    
          expect(user).toEqual(mockUser);
          expect(mockRepository.findById).toHaveBeenCalledWith("1");
        });
    
        it("should throw error if user not found", async () => {
          vi.mocked(mockRepository.findById).mockResolvedValue(null);
    
          await expect(service.getUser("999")).rejects.toThrow("User not found");
        });
      });
    
      describe("createUser", () => {
        it("should create user successfully", async () => {
          const userData = { name: "John", email: "john@example.com" };
          const createdUser = { id: "1", ...userData };
    
          vi.mocked(mockRepository.create).mockResolvedValue(createdUser);
    
          const user = await service.createUser(userData);
    
          expect(user).toEqual(createdUser);
          expect(mockRepository.create).toHaveBeenCalled();
        });
      });
    });
    

    Pattern 3: Spying on Functions

    // utils/logger.ts
    export const logger = {
      info: (message: string) => console.log(`INFO: ${message}`),
      error: (message: string) => console.error(`ERROR: ${message}`),
    };
    
    // services/order.service.ts
    import { logger } from "../utils/logger";
    
    export class OrderService {
      async processOrder(orderId: string): Promise<void> {
        logger.info(`Processing order ${orderId}`);
        // Process order logic
        logger.info(`Order ${orderId} processed successfully`);
      }
    }
    
    // services/order.service.test.ts
    import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
    import { OrderService } from "./order.service";
    import { logger } from "../utils/logger";
    
    describe("OrderService", () => {
      let service: OrderService;
      let loggerSpy: any;
    
      beforeEach(() => {
        service = new OrderService();
        loggerSpy = vi.spyOn(logger, "info");
      });
    
      afterEach(() => {
        loggerSpy.mockRestore();
      });
    
      it("should log order processing", async () => {
        await service.processOrder("123");
    
        expect(loggerSpy).toHaveBeenCalledWith("Processing order 123");
        expect(loggerSpy).toHaveBeenCalledWith("Order 123 processed successfully");
        expect(loggerSpy).toHaveBeenCalledTimes(2);
      });
    });
    

    Integration Testing

    Integration tests verify real database operations and HTTP endpoints using supertest and a test database instance. Always truncate tables in beforeEach and tear down in afterAll.

    For full API integration test examples (supertest + PostgreSQL) and database repository integration tests, see references/advanced-testing-patterns.md.

    Frontend Testing with Testing Library

    Test React components by rendering them and querying by role, placeholder, or test ID. Test hooks with renderHook + act. Prefer semantic queries (getByRole, getByPlaceholderText) over data-testid.

    For complete React component test examples (UserForm, hooks with renderHook/act), see references/advanced-testing-patterns.md.

    Test Fixtures and Factories

    Use @faker-js/faker to generate realistic test data factories. Factories accept optional overrides so tests can set only the fields they care about:

    // tests/fixtures/user.fixture.ts
    import { faker } from "@faker-js/faker";
    
    export function createUserFixture(overrides?: Partial<User>): User {
      return {
        id: faker.string.uuid(),
        name: faker.person.fullName(),
        email: faker.internet.email(),
        createdAt: faker.date.past(),
        ...overrides,
      };
    }
    

    For snapshot testing, coverage configuration, test organization patterns, promise testing, and timer mocking, see references/advanced-testing-patterns.md.

    Best Practices

    1. Follow AAA Pattern: Arrange, Act, Assert
    2. One assertion per test: Or logically related assertions
    3. Descriptive test names: Should describe what is being tested
    4. Use beforeEach/afterEach: For setup and teardown
    5. Mock external dependencies: Keep tests isolated
    6. Test edge cases: Not just happy paths
    7. Avoid implementation details: Test behavior, not implementation
    8. Use test factories: For consistent test data
    9. Keep tests fast: Mock slow operations
    10. Write tests first (TDD): When possible
    11. Maintain test coverage: Aim for 80%+ coverage
    12. Use TypeScript: For type-safe tests
    13. Test error handling: Not just success cases
    14. Use data-testid sparingly: Prefer semantic queries
    15. Clean up after tests: Prevent test pollution

    Reproducido de wshobson/agents bajo licencia MIT. Leer esta página en markdown.

    Archivos

    2 archivos en el paquete. Solo se lee SKILL.md al activarse — las referencias se cargan si el skill decide que las necesita.

    Antes de instalar

    Requiere Jest, Vitest, Testing Library, supertest y/o @faker-js/faker según el tipo de test que se implemente.

    Variables de entorno:EMAIL_FROMSMTP_HOSTSMTP_PASSSMTP_USER

    Detalles

    Creador
    wshobson
    Categoría
    Testing y QA
    Licencia
    MIT
    Recursos incluidos
    referencias
    Repositorio
    wshobson/agents
    Código fuente
    Ver SKILL.md

    Etiquetas

    Más de wshobson/agents

    Este repo incluye 180 skills. Si instalas uno, normalmente ya tienes los demás.

    Úsalo al seleccionar y colocar iconos, imágenes, SVGs, diagramas o infografías de apoyo aprobados en un PPTX editable.

    Costo de contexto al activarse
    344 tok
    Tamaño del paquete
    2 archivos
    Última actualización
    hace 26 días
    documentos

    Úsalo cuando pidan optimizar un prompt, mejorar su rendimiento, diseñar una plantilla, aplicar chain-of-thought, few-shot prompting o técnicas avanzadas de prompt engineering para producción.

    Costo de contexto al activarse
    1.3k tok
    Tamaño del paquete
    10 archivos
    Última actualización
    el mes pasado
    herramientas desarrollo

    Úsalo al redactar o reparar una especificación JSON con coordenadas explícitas para un PPTX editable.

    Costo de contexto al activarse
    489 tok
    Tamaño del paquete
    2 archivos
    Última actualización
    hace 26 días
    documentos

    Úsalo para validar o reparar un PPTX editable en cuanto a geometría, accesibilidad, editabilidad nativa, linaje de fuente e integridad del paquete OOXML.

    Costo de contexto al activarse
    409 tok
    Tamaño del paquete
    2 archivos
    Última actualización
    hace 26 días
    documentos

    Úsalo para analizar un PPTX de referencia en modo solo lectura: estructura, tema, tipografía, ritmo de layout, diagnósticos, catálogos de plantillas derivados o inspección segura del paquete OOXML.

    Costo de contexto al activarse
    689 tok
    Tamaño del paquete
    8 archivos
    Última actualización
    hace 26 días
    documentos

    Úsalo al preparar la narrativa, las fuentes y el contexto de diseño para un nuevo deck PPTX editable.

    Costo de contexto al activarse
    415 tok
    Tamaño del paquete
    2 archivos
    Última actualización
    hace 26 días
    documentos

    Skills relacionados

    Testea contratos inteligentes de forma exhaustiva con Hardhat y Foundry: tests unitarios, de integración y forking de mainnet.

    Costo de contexto al activarse
    2k tok
    Tamaño del paquete
    2 archivos
    Última actualización
    hace 2 meses
    testing qa

    Realiza auditorías de accesibilidad WCAG 2.2 con pruebas automatizadas, verificación manual y guía de remediación. Útil para auditar sitios, corregir violaciones y aplicar patrones de diseño accesible.

    Costo de contexto al activarse
    628 tok
    Tamaño del paquete
    2 archivos
    Última actualización
    hace 2 meses
    testing qa

    Prueba workflows de Temporal con pytest, time-skipping y estrategias de mocking: testing unitario, de integración, de replay y configuración de desarrollo local.

    Costo de contexto al activarse
    1.2k tok
    Tamaño del paquete
    5 archivos
    Última actualización
    hace 3 meses
    testing qa