NimbleMock: .NET용 현대적인 초고속 모킹 라이브러리 (Moq보다 34배 빠름)

발행: (2025년 12월 14일 오전 08:57 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

Cover image for NimbleMock: A Modern, Blazing-Fast Mocking Library for .NET (34x Faster Than Moq)

NimbleMock를 만든 이유

수년간 엔터프라이즈 .NET 애플리케이션에서 흔히 겪는 모킹 불편함을 경험했습니다:

  • Verbose setups 대형 인터페이스에 대한 설정이 장황함 (하나만 테스트하기 위해 20개 이상의 메서드를 모킹).
  • Performance hits 런타임 프록시(Castle.DynamicProxy in Moq) 때문에 발생하는 성능 저하 — 큰 테스트 스위트에서 생성 및 검증이 느림.
  • Static/sealed mocking hell — 유료 도구나 래퍼 없이 네이티브 지원이 없음.
  • Async and generics issues — 예상치 못한 null Task 또는 어색한 타입 추론.
  • Brittle tests — 로컬에서는 통과하지만 실제 API 형태와 맞지 않아 프로덕션에서 실패하는 모킹.

Moq는 검증된 라이브러리고, NSubstitute는 깔끔하고 가독성이 좋지만, 두 라이브러리 모두 소스 제너레이터 같은 최신 .NET 기능을 활용해 제로 오버헤드를 구현하지는 못합니다.

NimbleMock은 컴파일 타임 매직으로 이를 해결합니다: 런타임 프록시 없이, 공격적인 인라인 및 스택 할당을 사용합니다. 결과? MoQ 대비 34배 빠른 모크 생성3배 빠른 검증, 일반 시나리오에서 할당이 전혀 없습니다.

주요 기능

  • Partial mocks – 필요한 부분만 모킹하고, 모킹되지 않은 메서드는 NotImplementedException을 던져 초기에 감지합니다.
  • Native static/sealed mockingDateTime.Now, Environment 등을 직접 모킹합니다.
  • First‑class async supportTaskValueTask에 대한 매끄러운 지원.
  • Generic type inference – 중첩 제네릭(예: EF Core IQueryable)을 완전 지원합니다.
  • Fluent, chainable API – NSubstitute와 Moq의 장점을 결합한 체인형 API.
  • Lie‑proofing – 실제 스테이징 API와 비교해 오래된 모크 형태를 잡아내는 선택적 런타임 검증.
  • Compile‑time analyzers – 오류를 조기에 포착(예: async 메서드에 SetupAsync를 빼먹은 경우 경고).

MIT‑라이선스, 오픈소스이며 기여를 환영합니다!

빠른 시작

NuGet으로 설치

dotnet add package NimbleMock

기본 예제

using NimbleMock;

public interface IUserRepository
{
    User GetById(int id);
    Task SaveAsync(User user);
}

var mock = Mock.Of<IUserRepository>()
    .Setup(x => x.GetById(1), new User { Id = 1, Name = "Alice" })
    .SetupAsync(x => x.SaveAsync(default!), true)
    .Build();

var user = mock.Object.GetById(1); // Returns Alice

mock.Verify(x => x.GetById(1)).Once();

Partial mock (god‑interface에 최적)

var mock = Mock.Partial<IUserRepository>()
    .Only(x => x.GetData(1), expectedData)
    .Build();

// 모킹되지 않은 메서드는 안전을 위해 예외를 발생시킴

Static mocking

var staticMock = Mock.Static()
    .Returns(d => d.Now, new DateTime(2025, 12, 25))
    .Build();

staticMock.Verify(d => d.Now).Once();

Generics 예제

public interface IRepository
{
    IQueryable<User> Query();
}

var mock = Mock.Of<IRepository>()
    .Setup(x => x.Query(), users.AsQueryable())
    .Build();

var results = mock.Object.Query().Where(u => u.IsActive).ToList();

성능 벤치마크

.NET 8 (BenchmarkDotNet) 환경에서 실행.

Mock Creation & Setup

LibraryTime (ns)Memory AllocatedGain vs Moq
Moq48,81210.37 KBBaseline
NSubstitute9,93712.36 KB~5× faster
NimbleMock1,4153.45 KB34× faster

Method Execution Overhead

LibraryTime (µs)Gain vs Moq
Moq~1.4Baseline
NSubstitute~1.6Slower
NimbleMock~0.62.3× faster

Verification

LibraryTime (ns)Memory AllocatedGain vs Moq
Moq1,7952.12 KBBaseline
NSubstitute2,1632.82 KBSlower
NimbleMock5850.53 KB3× faster

소스 제너레이터 기반이므로 런타임 리플렉션이 전혀 없습니다!

벤치마크 직접 실행:

dotnet run --project tests/NimbleMock.Benchmarks --configuration Release

Moq/NSubstitute에서 마이그레이션

Moq에서

// Moq
var mock = new Mock<IUserRepository>();
mock.Setup(x => x.GetById(1)).Returns(user);
// NimbleMock
var mock = Mock.Of<IUserRepository>()
    .Setup(x => x.GetById(1), user)
    .Build();

검증도 체인형 API 덕분에 더 가독성이 좋습니다. 전체 마이그레이션 가이드는 레포지토리를 참고하세요.

Lie‑Proofing: “거짓 테스트” 방지

특징: 실제 엔드포인트와 모크를 비교 검증합니다.

var result = await LieProofing.AssertMatchesReal("https://api.staging.example.com");

if (!result.IsValid)
{
    // 불일치 로그 기록
}

프로덕션 장애 전에 API 변화를 잡아냅니다.

최종 생각

NimbleMock은 .NET 8/9+ 시대를 위해 설계되었습니다: 빠르고 안전하며 즐거운 TDD. 느린 테스트 스위트나 정적 모킹 해킹에 지쳤다면 한 번 사용해 보세요!

  • GitHub:
  • NuGet:

피드백을 환영합니다 — 현재 모킹 라이브러리에서 겪는 불편은 무엇인가요? 네이티브 정적 모킹과 이 속도를 위해 전환하시겠습니까?

⭐️ 레포지토리에 ⭐️를 눌러 주세요, 그리고 테스트를 다시 즐겁게 만들어 봅시다! 🚀

Back to Blog

관련 글

더 보기 »