본문 바로가기
카테고리 없음

✅ 레거시 코드에서 TDD 적용하기 | 2편: 외부 API 및 데이터베이스 Mocking

by bbongz 2025. 3. 26.

 

이전 글에서는 MSTest 환경 설정레거시 코드 리팩토링 과정을 통해 TDD(Test-Driven Development)를 적용하는 기본 전략을 다루었습니다. 이번 글에서는 외부 API 호출데이터베이스 접근이 포함된 코드를 Mocking하는 방법을 구체적으로 다룹니다.

  • ✅ 외부 API 호출을 Moq으로 Mocking하기
  • ✅ 데이터베이스 접근을 Mocking하기
  • ✅ Moq 설정 및 의존성 주입 적용

🚀 1. 레거시 코드에서 외부 API와 데이터베이스 호출 문제점

기존 레거시 코드에서 다음과 같은 문제가 발생할 수 있습니다:

  • ✅ 외부 API 호출이 코드에 직접 연결됨 → 테스트 불가능
  • ✅ 외부 API 응답 지연 → 테스트 속도 저하
  • ✅ 외부 시스템 상태에 따라 결과가 달라짐 → 테스트 결과 불안정
  • ✅ 데이터베이스 상태에 따라 테스트 결과가 달라짐 → 테스트 일관성 부족

🔥 기존 레거시 코드 예제


public static class OrderService
{
    public static bool ProcessOrder(int orderId)
    {
        var order = Database.GetOrderById(orderId);
        if (order == null) return false;

        var paymentResult = PaymentGateway.Charge(order.TotalAmount);
        if (!paymentResult) return false;

        Logger.Log($"Order {orderId} processed.");
        return true;
    }
}

🛠️ 2. 인터페이스 추가 및 코드 수정

외부 시스템에 대한 호출을 Mocking하기 위해 다음과 같은 순서로 코드 수정이 필요합니다:

  • ✅ 외부 API 호출 → 인터페이스 정의 후 주입
  • ✅ 데이터베이스 접근 → 인터페이스 정의 후 주입
  • ✅ Moq을 사용해 Mocking 처리

🚀 1) 인터페이스 정의

IDatabase.cs


public interface IDatabase
{
    Order GetOrderById(int orderId);
}

IPaymentGateway.cs


public interface IPaymentGateway
{
    bool Charge(decimal amount);
}

🚀 2) 코드 수정

의존성을 인터페이스를 통해 주입하고, Mocking이 가능하도록 코드 수정


public class OrderService
{
    private readonly IDatabase _database;
    private readonly IPaymentGateway _paymentGateway;

    public OrderService(IDatabase database, IPaymentGateway paymentGateway)
    {
        _database = database;
        _paymentGateway = paymentGateway;
    }

    public bool ProcessOrder(int orderId)
    {
        var order = _database.GetOrderById(orderId);
        if (order == null) return false;

        var paymentResult = _paymentGateway.Charge(order.TotalAmount);
        if (!paymentResult) return false;

        Logger.Log($"Order {orderId} processed.");
        return true;
    }
}

🚀 3. 외부 API 호출 Mocking

(1) Moq 설정

Moq는 NuGet에서 설치할 수 있습니다.


Install-Package Moq

(2) Moq으로 API 호출 Mocking


using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

[TestClass]
public class OrderServiceTests
{
    [TestMethod]
    public void Test_ProcessOrder_Success()
    {
        // Arrange
        var mockDatabase = new Mock<IDatabase>();
        mockDatabase.Setup(db => db.GetOrderById(It.IsAny<int>()))
                    .Returns(new Order { OrderId = 1, TotalAmount = 100 });

        var mockPaymentGateway = new Mock<IPaymentGateway>();
        mockPaymentGateway.Setup(pg => pg.Charge(It.IsAny<decimal>()))
                          .Returns(true);

        var service = new OrderService(mockDatabase.Object, mockPaymentGateway.Object);

        // Act
        var result = service.ProcessOrder(1);

        // Assert
        Assert.IsTrue(result);
    }
}

🚀 4. 실패 케이스 추가하기

📌 실패 케이스: 결제 실패


[TestMethod]
public void Test_ProcessOrder_Failure_PaymentFailed()
{
    // Arrange
    var mockDatabase = new Mock<IDatabase>();
    mockDatabase.Setup(db => db.GetOrderById(It.IsAny<int>()))
                .Returns(new Order { OrderId = 1, TotalAmount = 100 });

    var mockPaymentGateway = new Mock<IPaymentGateway>();
    mockPaymentGateway.Setup(pg => pg.Charge(It.IsAny<decimal>()))
                      .Returns(false);

    var service = new OrderService(mockDatabase.Object, mockPaymentGateway.Object);

    // Act
    var result = service.ProcessOrder(1);

    // Assert
    Assert.IsFalse(result);
}

🚀 5. 테스트 커버리지 점검

  • 👉 Visual Studio → 테스트 탐색기 → 커버리지 분석 실행
  • 👉 결과:
    • ✅ 성공 테스트 → 초록색 표시
    • ✅ 실패 테스트 → 빨간색 표시

🎯 결과

  • ✅ 외부 API 및 데이터베이스 호출 Mocking 완료
  • ✅ 성공 및 실패 테스트 작성 완료
  • ✅ Moq을 통해 테스트 결과 제어 완료
  • ✅ 코드 수정 없이 테스트 환경 제어 가능

🚀 다음 편 예고

👉 다음 글에서는 테스트 데이터베이스 설정통합 테스트 전략을 다루겠습니다.
👉 통합 테스트에서 실제 데이터베이스를 사용하는 경우의 문제점과 해결책을 다룹니다.