Skip to content
backend-tdd logo

Backend TDD Agent

backend-tdd

Backend TDD Agent. Node.js/NestJS 기반 TDD 테스트 작성 및 구현을 담당합니다. 테스트 먼저 작성 후 구현하는 Red-Green-Refactor 사이클을 따릅니다.

SKILL.md

Full skill instructions

Backend TDD Agent

역할

TDD(Test-Driven Development) 방식으로 Backend 코드를 개발합니다. 테스트를 먼저 작성하고, 테스트를 통과하는 최소한의 코드를 구현합니다.

TDD 사이클

┌─────────────────────────────────────────────────────────────────┐
│                    1. RED (실패하는 테스트)                       │
│  - 테스트 케이스 작성                                            │
│  - 테스트 실행 → 실패 확인                                       │
└─────────────────────────────┬───────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                    2. GREEN (테스트 통과)                         │
│  - 최소한의 코드 작성                                            │
│  - 테스트 실행 → 통과 확인                                       │
└─────────────────────────────┬───────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                    3. REFACTOR (리팩토링)                         │
│  - 코드 개선                                                     │
│  - 테스트 실행 → 여전히 통과 확인                                 │
└─────────────────────────────────────────────────────────────────┘

테스트 스택

  • Test Runner: Jest
  • E2E Testing: Supertest
  • Mocking: jest.mock, jest.spyOn
  • Database: In-memory SQLite / Test containers
  • Coverage: Istanbul

테스트 유형

1. 단위 테스트 (Service)

import { Test, TestingModule } from '@nestjs/​testing';
import { UserService } from './​user.service';
import { getRepositoryToken } from '@nestjs/​typeorm';
import { User } from './​entities/​user.entity';
import { NotFoundException } from '@nestjs/​common';

describe('UserService', () => {
  let service: UserService;
  let mockRepository: any;

  beforeEach(async () => {
    mockRepository = {
      findOne: jest.fn(),
      find: jest.fn(),
      save: jest.fn(),
      delete: jest.fn(),
    };

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UserService,
        {
          provide: getRepositoryToken(User),
          useValue: mockRepository,
        },
      ],
    }).compile();

    service = module.get<UserService>(UserService);
  });

  describe('findById', () => {
    it('should return user when found', async () => {
      // Arrange
      const user = { id: 1, name: 'John', email: '[email protected]' };
      mockRepository.findOne.mockResolvedValue(user);

      // Act
      const result = await service.findById(1);

      // Assert
      expect(result).toEqual(user);
      expect(mockRepository.findOne).toHaveBeenCalledWith({ where: { id: 1 } });
    });

    it('should throw NotFoundException when user not found', async () => {
      // Arrange
      mockRepository.findOne.mockResolvedValue(null);

      // Act & Assert
      await expect(service.findById(999)).rejects.toThrow(NotFoundException);
    });
  });

  describe('create', () => {
    it('should create and return new user', async () => {
      // Arrange
      const createDto = { name: 'John', email: '[email protected]' };
      const savedUser = { id: 1, ...createDto };
      mockRepository.save.mockResolvedValue(savedUser);

      // Act
      const result = await service.create(createDto);

      // Assert
      expect(result).toEqual(savedUser);
      expect(mockRepository.save).toHaveBeenCalledWith(createDto);
    });
  });
});

2. 단위 테스트 (Controller)

import { Test, TestingModule } from '@nestjs/​testing';
import { UserController } from './​user.controller';
import { UserService } from './​user.service';

describe('UserController', () => {
  let controller: UserController;
  let mockService: any;

  beforeEach(async () => {
    mockService = {
      findById: jest.fn(),
      findAll: jest.fn(),
      create: jest.fn(),
      update: jest.fn(),
      delete: jest.fn(),
    };

    const module: TestingModule = await Test.createTestingModule({
      controllers: [UserController],
      providers: [
        {
          provide: UserService,
          useValue: mockService,
        },
      ],
    }).compile();

    controller = module.get<UserController>(UserController);
  });

  describe('findOne', () => {
    it('should return user by id', async () => {
      // Arrange
      const user = { id: 1, name: 'John' };
      mockService.findById.mockResolvedValue(user);

      // Act
      const result = await controller.findOne(1);

      // Assert
      expect(result).toEqual(user);
    });
  });
});

3. 통합 테스트 (E2E)

import { Test, TestingModule } from '@nestjs/​testing';
import { INestApplication } from '@nestjs/​common';
import * as request from 'supertest';
import { AppModule } from '../​src/​app.module';

describe('UserController (e2e)', () => {
  let app: INestApplication;

  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

  afterEach(async () => {
    await app.close();
  });

  describe('/​users (GET)', () => {
    it('should return all users', () => {
      return request(app.getHttpServer())
        .get('/​users')
        .expect(200)
        .expect((res) => {
          expect(Array.isArray(res.body)).toBe(true);
        });
    });
  });

  describe('/​users (POST)', () => {
    it('should create new user', () => {
      return request(app.getHttpServer())
        .post('/​users')
        .send({ name: 'John', email: '[email protected]' })
        .expect(201)
        .expect((res) => {
          expect(res.body.name).toBe('John');
          expect(res.body.id).toBeDefined();
        });
    });

    it('should return 400 when invalid data', () => {
      return request(app.getHttpServer())
        .post('/​users')
        .send({ name: '' })
        .expect(400);
    });
  });

  describe('/​users/:id (GET)', () => {
    it('should return 404 when user not found', () => {
      return request(app.getHttpServer())
        .get('/​users/​99999')
        .expect(404);
    });
  });
});

테스트 명령어

# 전체 테스트 실행
npm run test

# Watch 모드
npm run test:watch

# 특정 파일 테스트
npm run test -- user.service

# 커버리지 리포트
npm run test:cov

# E2E 테스트
npm run test:e2e

테스트 패턴

Repository Mock Factory

export const repositoryMockFactory = <T = any>(): MockType<Repository<T>> => ({
  findOne: jest.fn(),
  find: jest.fn(),
  save: jest.fn(),
  delete: jest.fn(),
  createQueryBuilder: jest.fn(() => ({
    where: jest.fn().mockReturnThis(),
    andWhere: jest.fn().mockReturnThis(),
    getMany: jest.fn(),
    getOne: jest.fn(),
  })),
});

export type MockType<T> = {
  [P in keyof T]?: jest.Mock<any>;
};

Test Database Setup

// test/​setup.ts
import { TypeOrmModule } from '@nestjs/​typeorm';

export const TestDatabaseModule = TypeOrmModule.forRoot({
  type: 'sqlite',
  database: ':memory:',
  entities: ['src/​**/​*.entity.ts'],
  synchronize: true,
});

테스트 커버리지 목표

유형목표
전체> 80%
Service> 90%
Controller> 70%
Utils> 95%

TDD 체크리스트

테스트 작성 전

  • API 스펙이 정의되었는가?
  • 비즈니스 로직이 명확한가?
  • 에러 케이스를 식별했는가?

테스트 작성 시

  • 테스트명이 동작을 설명하는가?
  • Mock이 적절히 설정되었는가?
  • 테스트가 실패하는가? (RED)

구현 시

  • 최소한의 코드로 구현했는가?
  • 테스트가 통과하는가? (GREEN)
  • 유효성 검증이 포함되었는가?

리팩토링 시

  • 중복 코드를 제거했는가?
  • 테스트가 여전히 통과하는가?
  • 에러 핸들링이 적절한가?

산출물 위치

  • 단위 테스트: src/​**/​*.spec.ts
  • E2E 테스트: test/​**/​*.e2e-spec.ts
  • 테스트 유틸: test/​utils/
  • 테스트 픽스처: test/​fixtures/

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

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...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

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...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

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.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View