소프트웨어 개발에서 단위 테스트(Unit Test)는 코드의 안정성과 품질을 유지하는 중요한 방법입니다. C#과 .NET 환경에서 단위 테스트를 효과적으로 작성하기 위해 다양한 Mocking 프레임워크가 존재하는데, 그 중 가장 널리 사용되는 도구 중 하나가 바로 Moq입니다. 이 글에서는 Moq의 개념과 특징, 그리고 Moq를 활용한 단위 테스트 작성 방법을 자세히 소개하겠습니다.
Moq는 .NET 개발 환경에서 객체의 동작을 모방(Mock)하여 테스트를 쉽게 작성할 수 있도록 도와주는 프레임워크입니다. 테스트 대상 객체의 의존성을 가짜(Mock) 객체로 대체하여, 독립적이고 효율적인 테스트를 가능하게 합니다.
Moq는 NuGet 패키지를 통해 손쉽게 설치할 수 있습니다. 다음 명령어를 통해 설치할 수 있습니다.
Install-Package Moq
또는 .NET Core 프로젝트에서는 다음과 같이 설치할 수 있습니다.
dotnet add package Moq
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
Moq는 특정 메서드가 예상대로 호출되었는지 검증할 수 있는 기능을 제공합니다.
mock.Object.Add(2, 3);
// Add 메서드가 (2, 3) 인자로 한 번 호출되었는지 검증
mock.Verify(calc => calc.Add(2, 3), Times.Once());
특정 조건에서 예외를 발생시키도록 설정할 수도 있습니다.
mock.Setup(calc => calc.Add(It.IsAny<int>(), -1))
.Throws<ArgumentException>();
// 예외 발생
try
{
mock.Object.Add(5, -1);
}
catch (ArgumentException)
{
Console.WriteLine("ArgumentException 발생!");
}
아래는 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);
}
}
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을 잘 활용하면 보다 신뢰할 수 있는 소프트웨어를 개발할 수 있습니다.