Moq란 무엇인가?

용과젤리·2025년 1월 18일
0

소프트웨어 개발에서 단위 테스트(Unit Test)는 코드의 안정성과 품질을 유지하는 중요한 방법입니다. C#과 .NET 환경에서 단위 테스트를 효과적으로 작성하기 위해 다양한 Mocking 프레임워크가 존재하는데, 그 중 가장 널리 사용되는 도구 중 하나가 바로 Moq입니다. 이 글에서는 Moq의 개념과 특징, 그리고 Moq를 활용한 단위 테스트 작성 방법을 자세히 소개하겠습니다.

Moq란?

Moq는 .NET 개발 환경에서 객체의 동작을 모방(Mock)하여 테스트를 쉽게 작성할 수 있도록 도와주는 프레임워크입니다. 테스트 대상 객체의 의존성을 가짜(Mock) 객체로 대체하여, 독립적이고 효율적인 테스트를 가능하게 합니다.

Moq의 특징

  • 간결한 문법: 람다 식(Lambda Expression)을 활용한 직관적인 문법 제공
  • 유연한 설정: 메서드 호출, 반환값 설정, 예외 처리 등 다양한 동작을 자유롭게 설정 가능
  • Verify 기능: 특정 메서드나 속성이 예상대로 호출되었는지 검증 가능
  • NuGet 패키지 지원: 간편한 설치 및 최신 업데이트 관리

Moq 설치 방법

Moq는 NuGet 패키지를 통해 손쉽게 설치할 수 있습니다. 다음 명령어를 통해 설치할 수 있습니다.

Install-Package Moq

또는 .NET Core 프로젝트에서는 다음과 같이 설치할 수 있습니다.

dotnet add package Moq

Moq의 기본 사용법

1. 인터페이스 Mock 객체 생성

Moq는 주로 인터페이스나 추상 클래스를 Mocking하는 데 사용됩니다.

using Moq;

// 인터페이스 정의
public interface ICalculator
{
    int Add(int a, int b);
}

// Mock 객체 생성
var mock = new Mock<ICalculator>();

// 메서드 동작 설정
mock.Setup(calc => calc.Add(2, 3)).Returns(5);

// Mock 객체 사용
int result = mock.Object.Add(2, 3);
Console.WriteLine(result);  // 출력: 5

2. 메서드 호출 검증 (Verify)

Moq는 특정 메서드가 예상대로 호출되었는지 검증할 수 있는 기능을 제공합니다.

mock.Object.Add(2, 3);

// Add 메서드가 (2, 3) 인자로 한 번 호출되었는지 검증
mock.Verify(calc => calc.Add(2, 3), Times.Once());

3. 예외 발생 설정

특정 조건에서 예외를 발생시키도록 설정할 수도 있습니다.

mock.Setup(calc => calc.Add(It.IsAny<int>(), -1))
    .Throws<ArgumentException>();

// 예외 발생
try
{
    mock.Object.Add(5, -1);
}
catch (ArgumentException)
{
    Console.WriteLine("ArgumentException 발생!");
}

ServerWrapper 클래스 소개 및 테스트

아래는 SQL Server 인스턴스를 감싸는 ServerWrapper 클래스입니다. 이 클래스의 단위 테스트는 Moq와 NUnit을 활용하여 작성할 수 있습니다.

public class ServerWrapper : IServer
{
    private readonly Server _server;

    public ServerWrapper(Server server)
    {
        _server = server ?? throw new ArgumentNullException(nameof(server));
    }

    public bool ContainsDatabase(string databaseName)
    {
        return _server.Databases.Contains(databaseName);
    }

    public Database GetDatabase(string databaseName)
    {
        return _server.Databases[databaseName];
    }

    public void AddServerMessageHandler(Action<ServerMessageEventArgs> handler)
    {
        if (handler == null)
            throw new ArgumentNullException(nameof(handler));
        
        _server.ConnectionContext.ServerMessage += (sender, e) => handler(e);
    }
}

ServerWrapper 단위 테스트 예제

using Moq;
using NUnit.Framework;
using Microsoft.SqlServer.Management.Smo;

[TestFixture]
public class ServerWrapperTests
{
    private Mock<Server> _mockServer;
    private Mock<DatabaseCollection> _mockDatabases;
    private ServerWrapper _serverWrapper;

    [SetUp]
    public void Setup()
    {
        _mockServer = new Mock<Server>();
        _mockDatabases = new Mock<DatabaseCollection>(null);
        _mockServer.Setup(s => s.Databases).Returns(_mockDatabases.Object);
        _serverWrapper = new ServerWrapper(_mockServer.Object);
    }

    [Test]
    public void ContainsDatabase_WhenDatabaseExists_ReturnsTrue()
    {
        // Arrange
        _mockDatabases.Setup(d => d.Contains("TestDB")).Returns(true);
        
        // Act
        bool result = _serverWrapper.ContainsDatabase("TestDB");
        
        // Assert
        Assert.That(result, Is.True);
    }

    [Test]
    public void AddServerMessageHandler_NullHandler_ThrowsArgumentNullException()
    {
        Assert.Throws<ArgumentNullException>(() => _serverWrapper.AddServerMessageHandler(null));
    }
}

참고 자료

결론

Moq는 단위 테스트에서 객체 간의 의존성을 효과적으로 처리할 수 있도록 도와주는 강력한 Mocking 프레임워크입니다. 간결한 문법과 강력한 기능 덕분에 테스트 코드 작성이 훨씬 효율적이고 직관적으로 변합니다. Moq와 NUnit을 잘 활용하면 보다 신뢰할 수 있는 소프트웨어를 개발할 수 있습니다.

profile
C#, .Net 개발자입니다.

0개의 댓글