Receber por email
← Voltar para o catálogo

allra-test-writing

Allra 백엔드 테스트 작성 표준. Use when writing test code, choosing test helpers, generating test data with Fixture Monkey, or verifying test coverage.

367estrelas
Atualizado há 4 meses

Ver no GitHub ↗

Como adicionar

/plugin marketplace add aiskillstore/marketplace

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/allra-test-writing/svg)](https://www.skillteca.com.br/skills/allra-test-writing)

Skills relacionadas

Ver todas de Segurança →

Alerta por categoria

Receba novas skills de Segurança toda segunda

1 email curto, só com as skills novas de Segurança. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Allra Test Writing Standards

Allra 백엔드 팀의 테스트 작성 표준을 정의합니다. 테스트 헬퍼 선택, Fixture Monkey 데이터 생성, Given-When-Then 패턴, AssertJ 검증을 포함합니다.

프로젝트 기본 정보

이 가이드는 다음 환경을 기준으로 작성되었습니다:

  • Java: 17 이상
  • Spring Boot: 3.2 이상
  • Testing Framework: JUnit 5
  • Assertion Library: AssertJ
  • Mocking: Mockito
  • Test Data: Fixture Monkey (선택 사항)
  • Container: Testcontainers (선택 사항)

참고: 프로젝트별로 사용하는 라이브러리나 버전이 다를 수 있습니다. 프로젝트에 맞게 조정하여 사용하세요.

테스트 헬퍼 선택 가이드

주의: 아래 테스트 헬퍼는 Allra 표준 템플릿에서 제공됩니다. 프로젝트에 이러한 헬퍼가 없는 경우, Spring Boot 기본 테스트 어노테이션(@SpringBootTest, @DataJpaTest, @WebMvcTest 등)을 직접 사용하되, 이 가이드의 테스트 패턴과 원칙은 동일하게 적용합니다.

| 헬퍼 | 태그 | 용도 | 무게 | 언제? | |------|------|------|------|-------| | IntegrationTest | Integration | 여러 서비스 통합 | 🔴 무거움 | 전체 워크플로우 | | RdbTest | RDB | Repository, QueryDSL | 🟡 중간 | 쿼리 검증 | | ControllerTest | Controller | API 엔드포인트 | 🟢 가벼움 | REST API 검증 | | RedisTest | Redis | Redis 캐싱 | 🟢 가벼움 | 캐시 검증 | | MockingUnitTest | MockingUnit | Service 단위 | 🟢 매우 가벼움 | 비즈니스 로직 | | PojoUnitTest | PojoUnit | 도메인 로직 | 🟢 매우 가벼움 | 순수 자바 |

선택 플로우

API 엔드포인트? → ControllerTest
여러 서비스 통합? → IntegrationTest
Repository/QueryDSL? → RdbTest
Redis 캐싱? → RedisTest
Service 로직 (Mock)? → MockingUnitTest
도메인 로직 (POJO)? → PojoUnitTest

🎯 Mock vs Integration 선택 기준 (중요!)

원칙: 기본은 MockingUnitTest, 꼭 필요할 때만 IntegrationTest

목표: IntegrationTest 비율 5% 이하 유지

의사결정 플로우차트

┌─────────────────────────────────┐
│ 무엇을 테스트하려고 하는가?    │
└────────────┬────────────────────┘
             │
    ┌────────▼────────┐
    │ 도메인 로직만?  │ ──Yes──> PojoUnitTest
    └────────┬────────┘
             │ No
    ┌────────▼─────────────────────┐
    │ Repository/QueryDSL 쿼리?   │ ──Yes──> RdbTest
    └────────┬─────────────────────┘
             │ No
    ┌────────▼─────────────────────┐
    │ API 엔드포인트 응답/검증?   │ ──Yes──> ControllerTest
    └────────┬─────────────────────┘
             │ No
    ┌────────▼─────────────────────────────┐
    │ Service 비즈니스 로직 검증?         │
    └────────┬─────────────────────────────┘
             │
    ┌────────▼──────────────────────────────────────────┐
    │ 다음 중 하나라도 해당하는가?                      │
    │                                                   │
    │ 1. 💰 금전 처리 (입금/출금/이체/환불)            │
    │ 2. 🔄 트랜잭션 롤백이 중요한 워크플로우           │
    │ 3. 📊 여러 테이블 데이터 정합성 검증             │
    │ 4. 🔐 실제 DB 제약조건 검증 필수                 │
    │ 5. 📝 복잡한 상태 전이 (3단계 이상)              │
    │ 6. 🎯 이벤트 발행/리스너 통합 검증               │
    │ 7. 🤝 3개 이상 서비스 필수 협력                  │
    └────┬──────────────────────────────────────┬────────┘
         │ Yes                                  │ No
         │                                      │
    ┌────▼────────────┐              ┌─────────▼──────────┐
    │ IntegrationTest │              │ MockingUnitTest    │
    │ (최소화)        │              │ (기본 선택)       │
    └─────────────────┘              └────────────────────┘

IntegrationTest가 필요한 구체적인 케이스

✅ 1. 금전 처리 (입금/출금/이체/환불)

이유: 돈이 관련된 로직은 실제 DB 트랜잭션 동작 검증 필수

// 예시: 펀딩 신청 (FsData → FsPayment → PointUsage → UserAccount 연계)
@DisplayName("펀딩 신청 시 금액 차감 및 결제 생성")
class ApplyServiceIntegrationTest extends IntegrationTest {

    @Test
    @Transactional
    void apply_DecreasesAmount_Success() {
        // given: 사용자 잔액 100만원
        User user = createUserWithBalance(1_000_000);

        // when: 50만원 펀딩 신청
        applyService.apply(new ApplyRequest(user.getId(), 500_000));

        // then: 실제 DB에서 잔액 50만원 확인
        User updated = userRepository.findById(user.getId()).get();
        assertThat(updated.getBalance()).isEqualTo(500_000);

        // then: FsPayment 생성 확인
        FsPayment payment = fsPaymentRepository.findByUserId(user.getId()).get();
        assertThat(payment.getAmount()).isEqualTo(500_000);
    }
}

✅ 2. 트랜잭션 롤백이 중요한 워크플로우

이유: 실패 시 모든 작업이 원자적으로 롤백되어야 함

// 예시: 결제 실패 시 전체 롤백
@Test
@DisplayName("결제 실패 시 신청 데이터도 롤백")
void apply_PaymentFails_RollbackAll() {
    // given
    User user = createUser();
    mockPaymentGateway_ToFail(); // 외부 결제는 Mock으로

    // when & then
    assertThatThrownBy(() -> applyService.apply(request))
        .isInstanceOf(PaymentException.class);

    // then: DB에 어떤 데이터도 저장되지 않음
    assertThat(fsDataRepository.findAll()).isEmpty();
    assertThat(fsPaymentRepository.findAll()).isEmpty();
}

참고: 외부 연동(결제 게이트웨이, 외부 API)은 @MockBean으로 처리

✅ 3. 여러 테이블 데이터 정합성 검증

이유: 관련된 모든 테이블의 상태가 일관되게 유지되는지 확인

// 예시: 계약 생성 시 UserAccount, Contract, FsData 모두 생성
@Test
@DisplayName("신규 계약 시 관련 테이블 모두 생성")
void createContract_CreatesAllRelatedData() {
    // when
    contractService.createContract(userId, contractType);

    // then: 3개 테이블 모두 데이터 존재
    assertThat(userAccountRepository.findByUserId(userId)).isPresent();
    assertThat(contractRepository.findByUserId(userId)).isPresent();
    assertThat(fsDataRepository.findByUserId(userId)).isPresent();
}

✅ 4. 실제 DB 제약조건 검증

이유: Unique, FK, Check 제약조건은 실제 DB에서만 확인 가능

// 예시: 중복 계좌 등록 방지
@Test
@DisplayName("동일 계좌번호 중복 등록 시 예외")
void registerAccount_Duplicate_ThrowsException() {
    // given
    userAccountRepository.save(new UserAccount(userId, "123-456-789"));

    // when & then: Unique 제약조건 위반
    assertThatThrownBy(() ->
        userAccountRepository.save(new UserAccount(userId, "123-456-789"))
    ).isInstanceOf(DataIntegrityViolationException.class);
}

✅ 5. 복잡한 상태 전이 (3단계 이상)

이유: 상태 변화 흐름을 실제 시나리오대로 검증

// 예시: 계약 상태 전이 (신청 → 심사 → 승인 → 완료)
@Test
@DisplayName("계약 워크플로우 전체 검증")
void contractWorkflow_FullCycle() {
    // given: 신청
    Contract contract = contractService.create(userId);
    assertThat(contract.getStatus()).isEqualTo(ContractStatus.PENDING);

    // when: 심사
    contractService.review(contract.getId());
    // then
    Contract reviewed = contractRepository.findById(contract.getId()).get();
    assertThat(reviewed.getStatus()).isEqualTo(ContractStatus.REVIEWED);

    // when: 승인
    contractService.approve(contract.getId());
    // then
    Contract approved = contractRepository.findById(contract.getId()).get();
    assertThat(approved.getStatus()).isEqualTo(ContractStatus.APPROVED);
}

✅ 6. 이벤트 발행/리스너 통합 검증

이유: 이벤트가 실제로 발행되고 리스너가 동작하는지 확인

// 예시: 계약 완료 이벤트 → 알림 발송
@Test
@DisplayName("계약 완료 시 알림 이벤트 발행")
void completeContract_PublishesEvent() {
    // given
    Contract contract = createContract(userId);

    // when
    contractService.complete(contract.getId());

    // then: 실제로 알림이 발송되었는가? (외부 알림은 @MockBean)
    verify(notificationService).sendContractCompleteNotification(userId);
}

✅ 7. 3개 이상 서비스가 필수적으로 협력

이유: 서비스 간 상호작용을 실제 환경에서 검증

// 예시: 주문 생성 → 재고 차감 → 결제 → 알림
@Test
@DisplayName("주문 생성 워크플로우")
void createOrder_FullWorkflow() {
    // given
    Product product = createProductWithStock(100);

    // when
    orderService.createOrder(userId, product.getId(), 10);

    // then: 재고 차감
    Product updated = productRepository.findById(product.getId()).get();
    assertThat(updated.getStock()).isEqualTo(90);

    // then: 결제 생성
    Payment payment = paymentRepository.findByUserId(userId).get();
    assertThat(payment.getStatus()).isEqualTo(PaymentStatus.COMPLETED);
}

MockingUnitTest로 충분한 케이스

✅ 대부분의 Service 로직

  • 단순 조회 (findById, findAll)
  • 데이터 변환/계산
  • 검증 로직 (validation)
  • 단일 엔티티 CRUD
  • 비즈니스 규칙 검증
// 예시: 할인율 계산 로직 (Mock으로 충분)
@ExtendWith(MockitoExtension.class)
class DiscountServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private DiscountService discountService;

    @Test
    @DisplayName("VIP 회원 10% 할인 계산")
    void calculateDiscount_VipUser_10Percent() {
        // given
        User vipUser = User.builder().grade("VIP").build();
        when(userRepository.findById(1L)).thenReturn(Optional.of(vipUser));

        // when
        BigDecimal discount = discountService.calculate
Compartilhar X LinkedIn