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

📝 C# MSTest에서 Moq를 활용한 Mock 테스트 및 DI(의존성 주입) 전략

by bbongz 2025. 3. 23.

 

이전 글에서는 경계 값 테스트예외 처리를 통해 TDD 사이클을 완성했습니다.
이번 글에서는 Moq를 활용한 Mock 테스트DI(의존성 주입) 전략을 통해 테스트의 유연성을 높이는 방법을 다루겠습니다. 😎

이번 글에서는 다음 내용을 중점적으로 다룹니다:
✔️ Moq 프레임워크 설정 및 설치
✔️ Mock 객체 생성 및 주입
✔️ DI(Dependency Injection)를 통한 테스트 유연성 강화


Mock 테스트란?

Mock 테스트는 실제 객체 대신 가짜 객체(Mock) 를 만들어 테스트하는 기법입니다.
Mock 객체를 사용하면 외부 의존성에 영향을 받지 않고 독립적으로 단위 테스트를 수행할 수 있습니다.

🎯 Mock 테스트가 필요한 경우

  • 데이터베이스 호출이 필요한 경우
  • 외부 API 호출이 필요한 경우
  • 네트워크 연결이 필요한 경우
  • 파일 시스템 접근이 필요한 경우

👉 Mock 객체를 사용하면 테스트 속도 향상테스트 독립성 강화가 가능합니다.


Moq 프레임워크란?

Moq는 C#에서 가장 널리 사용되는 Mock 프레임워크입니다.
테스트 대상 객체의 동작을 쉽게 시뮬레이션할 수 있습니다.

Moq 주요 기능:
✔️ 메서드 호출 시 동작 정의
✔️ 반환 값 설정
✔️ 호출 횟수 검증
✔️ 예외 발생 시뮬레이션


🚀 1. Moq 설치하기

🛠️ NuGet 패키지 설치

NuGet 패키지 관리자에서 다음 명령어 입력:

dotnet add package Moq

또는 Visual Studio에서 다음과 같이 설치합니다:

  1. 도구 → NuGet 패키지 관리자 → 패키지 관리
  2. Moq 검색 후 설치

🚀 2. DI(의존성 주입) 적용하기

🔧 Calculator 클래스를 인터페이스로 리팩토링

의존성 주입을 사용하려면 인터페이스를 정의해야 합니다.
기존 Calculator 클래스를 다음과 같이 수정합니다.


인터페이스 정의 (ICalculator.cs)

public interface ICalculator
{
    int Add(int a, int b);
    int Subtract(int a, int b);
    int Multiply(int a, int b);
    int Divide(int a, int b);
}

Calculator 클래스 수정 (Calculator.cs)

public class Calculator : ICalculator
{
    public int Add(int a, int b) => a + b;
    public int Subtract(int a, int b) => a - b;
    public int Multiply(int a, int b) => a * b;
    public int Divide(int a, int b)
    {
        if (b == 0)
            throw new DivideByZeroException("Cannot divide by zero.");

        return a / b;
    }
}

🚀 3. Moq를 사용한 Mock 테스트 작성하기

이제 Moq를 활용해 Mock 객체를 만들어 보겠습니다.
ICalculator 인터페이스를 기반으로 Mock 객체를 생성하고 동작을 정의합니다.


기본 Mock 테스트 코드 작성

CalculatorTests.cs 파일 수정

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

namespace TDDExample.Tests
{
    [TestClass]
    public class CalculatorTests
    {
        private Mock<ICalculator> _mockCalculator;

        [TestInitialize]
        public void Setup()
        {
            _mockCalculator = new Mock<ICalculator>();
        }

        [TestMethod]
        public void Add_ShouldReturnSumOfTwoNumbers()
        {
            // Arrange
            _mockCalculator.Setup(m => m.Add(2, 3)).Returns(5);

            // Act
            int result = _mockCalculator.Object.Add(2, 3);

            // Assert
            Assert.AreEqual(5, result);
            _mockCalculator.Verify(m => m.Add(2, 3), Times.Once());
        }
    }
}

테스트 코드 설명

  1. Mock<ICalculator> → Moq 프레임워크에서 Mock 객체 생성
  2. Setup → Mock 객체의 동작 설정
  3. Returns → 동작 수행 시 반환값 설정
  4. Verify → 메서드 호출 횟수 검증

🚀 4. 복잡한 동작 정의하기

복잡한 로직에서도 Mock 객체를 통해 동작을 세밀하게 정의할 수 있습니다.
예를 들어 다음과 같은 동작을 정의할 수 있습니다:

다양한 입력 값 처리

_mockCalculator.Setup(m => m.Add(It.IsAny<int>(), It.IsAny<int>()))
    .Returns((int x, int y) => x + y);
  • It.IsAny<int>() → 어떤 정수 입력 값에도 반응
  • 람다식을 사용해 입력 값에 따라 동작 정의

예외 발생 시뮬레이션

_mockCalculator.Setup(m => m.Divide(It.IsAny<int>(), 0))
    .Throws(new DivideByZeroException());
  • 특정 입력 값에서 예외 발생하도록 설정
  • Divide 메서드가 0으로 나눌 때 예외 발생

호출 횟수 검증

_mockCalculator.Verify(m => m.Add(2, 3), Times.Once());
  • Times.Once() → 한 번만 호출되었는지 검증
  • 호출 횟수에 따라 다른 값 반환 가능

🚀 5. 의존성 주입 적용하기

CalculatorService 클래스 생성

실제 서비스 클래스에서 ICalculator를 의존성 주입받아 사용합니다.

public class CalculatorService
{
    private readonly ICalculator _calculator;

    public CalculatorService(ICalculator calculator)
    {
        _calculator = calculator;
    }

    public int CalculateSum(int a, int b)
    {
        return _calculator.Add(a, b);
    }
}

테스트에서 DI 적용

[TestMethod]
public void CalculateSum_ShouldReturnSumUsingMock()
{
    // Arrange
    _mockCalculator.Setup(m => m.Add(2, 3)).Returns(5);
    var service = new CalculatorService(_mockCalculator.Object);

    // Act
    int result = service.CalculateSum(2, 3);

    // Assert
    Assert.AreEqual(5, result);
}

리팩토링 단계

  • 인터페이스 정의 → 코드 유연성 강화
  • Mock 객체 → 외부 의존성 제거 및 테스트 독립성 확보
  • DI → 코드 재사용성 및 테스트 효율성 강화

👉 테스트 코드 최종 버전


🎯 마무리 및 다음 단계

지금까지 다음 내용을 완성했습니다:
✔️ Moq를 사용한 Mock 객체 생성 및 활용
✔️ 다양한 입력 값 및 호출 횟수 검증
✔️ 의존성 주입을 통한 테스트 유연성 강화

다음 글에서는 TDD에서 발생할 수 있는 문제와 해결 전략을 다루겠습니다.

 

👉 다음 글: C# MSTest에서 발생하는 테스트 문제 및 해결 전략 (5편)