[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"quiz-nestjs-weekly-testing-provider-overrides":44,"search-suggestions":60,"quiz-article-nestjs-weekly-testing-provider-overrides":106},[4,20,32],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Read it. Run it. Prove it.","A post a day on modern web development — most with an editable playground and a quiz that explains every answer. Free, no account needed.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Grammar explained the way good documentation explains an API — one idea at a time, each with a quiz.","amber-400","⌘",[13,15,29],"es",{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":17,"packages":18,"community":17,"certificates":17,"teams":18,"commerce":18},2,{"id":33,"slug":34,"name":35,"tagline":36,"description":37,"accentFrom":38,"accentTo":39,"icon":40,"defaultLocale":13,"locales":41,"features":42,"position":43},"7b3c16f2-931d-410e-802e-e1fa4edab7de","soft","Soft Skills","The half of the job nobody wrote documentation for.","Weekly, on the parts of working life that decide more than your code does — first weeks, meetings, interviews, promotions, and the people around you. Written from what actually happens, and recorded as a podcast you can listen to on the walk.","emerald-400","teal-300","◉",[13],{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":18},3,{"id":45,"slug":46,"kind":47,"title":48,"description":49,"config":50,"verticalId":5,"vertical":55,"course":52,"_count":56,"access":57,"attempts":59,"questionCount":51},"01a0a630-c936-700c-99fd-210aa7b61238","nestjs-weekly-testing-provider-overrides","PRACTICE_QUIZ","NestJS Testing Module: Provider Overrides","Test yourself on how @nestjs\u002Ftesting really resolves providers: overrideProvider, useMocker, and why get() throws for scoped providers when resolve() doesn't.",{"questionCount":51,"timeLimitSec":52,"shuffleQuestions":18,"shuffleOptions":17,"negativeMarking":19,"passScorePct":53,"maxAttempts":52,"revealAnswers":54,"allowFlagging":18,"allowBacktracking":17},10,null,70,"AFTER_SUBMIT",{"slug":6,"name":7},{"questions":51},{"allowed":17,"reason":58},"FREE",[],[61,65,69,73,77,81,85,89,92,96,99,103],{"slug":62,"name":63,"articles":64},"webdev","Webdev",100,{"slug":66,"name":67,"articles":68},"javascript","Javascript",83,{"slug":70,"name":71,"articles":72},"frontend","Frontend",71,{"slug":74,"name":75,"articles":76},"css","Css",34,{"slug":78,"name":79,"articles":80},"tutorial","Tutorial",28,{"slug":82,"name":83,"articles":84},"typescript","Typescript",15,{"slug":86,"name":87,"articles":88},"react","React",12,{"slug":90,"name":91,"articles":88},"performance","Performance",{"slug":93,"name":94,"articles":95},"node","Node",9,{"slug":97,"name":98,"articles":95},"browser","Browser",{"slug":100,"name":101,"articles":102},"accessibility","Accessibility",6,{"slug":104,"name":105,"articles":102},"grammar","Grammar",{"id":107,"slug":46,"title":108,"subtitle":52,"excerpt":109,"coverUrl":110,"locale":13,"readingMinutes":111,"publishedAt":112,"viewCount":113,"likeCount":19,"commentCount":19,"author":114,"vertical":119,"topic":120,"tags":123,"_count":128,"playground":130,"body":132,"bodyMd":479,"seo":480,"translationGroupId":482,"series":483,"podcastUrl":52,"verticalId":5,"thread":501,"assessments":503,"translations":506,"quiz":508},"01a0a630-c856-72d3-bae5-3339e38a8564","NestJS Testing Module: Provider Overrides (with Cheat Sheet)","A complete guide to NestJS's testing module: how overrideProvider really wires mocks, why get() fails on scoped providers, and using useMocker.","\u002Fmedia\u002Fcovers\u002Fnestjs-weekly-testing-provider-overrides.png",13,"2026-09-18T10:54:07.724Z",20,{"id":115,"name":116,"username":117,"avatarUrl":52,"headline":118},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":121,"name":122},"nestjs","Nestjs",[124,125,126,127],{"slug":121,"name":122,"color":52},{"slug":93,"name":94,"color":52},{"slug":82,"name":83,"color":52},{"slug":78,"name":79,"color":52},{"assessments":129},1,{"slug":46,"title":131},"NestJS testing module — interactive playground",{"blocks":133,"version":129},[134,138,144,147,150,155,158,167,170,173,176,191,194,197,202,205,209,212,215,218,221,224,227,231,234,237,240,244,247,250,253,257,260,263,266,270,273,276,279,282,286,289,294,297,300,303,307,310,313,321,324,332,335,339,342,345,348,351,354,357,360,363,366,369,372,375,378,381,384,432,435,438,441,444,447,450,458,461,464,467,470,473],{"id":135,"html":136,"type":137},"b1","\u003Cp>A unit test for \u003Ccode>OrdersController\u003C\u002Fcode> calls \u003Ccode>Test.createTestingModule({...}).compile()\u003C\u002Fcode>, then \u003Ccode>moduleRef.get(AuditLogService)\u003C\u002Fcode> to grab the mock and assert it was called. Every other provider in the test resolves without a hitch. This one throws:\u003C\u002Fp>","paragraph",{"id":139,"code":140,"type":141,"language":142,"highlight":143},"b2","Error: AuditLogService is a scoped provider. Use \"moduleRef.resolve()\" instead of \"moduleRef.get()\".","code","plain",[],{"id":145,"html":146,"type":137},"b3","\u003Cp>Nothing about the test setup looks wrong. \u003Ccode>AuditLogService\u003C\u002Fcode> is in the \u003Ccode>providers\u003C\u002Fcode> array. It was overridden with \u003Ccode>useValue\u003C\u002Fcode>. The override even worked — the controller under test is using the mock, not the real thing. The one line that&#39;s wrong is the \u003Cem>retrieval\u003C\u002Fem> method, and until you understand what \u003Ccode>get()\u003C\u002Fcode> and \u003Ccode>resolve()\u003C\u002Fcode> are each actually reaching into, the fix looks like superstition: &quot;just switch to \u003Ccode>resolve()\u003C\u002Fcode> and it works,&quot; without knowing why.\u003C\u002Fp>",{"id":148,"html":149,"type":137},"b4","\u003Cp>This is written against \u003Cstrong>NestJS 12.0.x\u003C\u002Fstrong> (verified September 2026, current \u003Ccode>@nestjs\u002Fcore\u003C\u002Fcode> release). Everything here — \u003Ccode>Test.createTestingModule\u003C\u002Fcode>, the override methods, \u003Ccode>get()\u003C\u002Fcode>\u002F\u003Ccode>resolve()\u003C\u002Fcode> — has been stable across the v9–v12 line; nothing in this article depends on a v12-specific change.\u003C\u002Fp>",{"id":151,"html":152,"text":153,"type":154,"level":31},"b5","What you&#39;ll learn","What you'll learn","heading",{"id":156,"html":157,"type":137},"b6","\u003Cp>By the end of this article you&#39;ll be able to:\u003C\u002Fp>",{"id":159,"type":160,"items":161,"ordered":18},"b7","list",[162,163,164,165,166],"Explain what a NestJS testing module actually \u003Cem>is\u003C\u002Fem> — not a mock registry, a real DI container","Use \u003Ccode>overrideProvider().useValue()\u002F.useClass()\u002F.useFactory()\u003C\u002Fcode> to swap a dependency without breaking the rest of the module&#39;s wiring","Know exactly when \u003Ccode>module.get()\u003C\u002Fcode> works and when it throws, and reach for \u003Ccode>module.resolve()\u003C\u002Fcode> correctly when it does","Use \u003Ccode>useMocker\u003C\u002Fcode> to stop hand-writing boilerplate mocks — and know when \u003Cem>not\u003C\u002Fem> to","Override a guard, pipe, interceptor, or filter for an end-to-end test, not just a constructor-injected provider",{"id":168,"html":169,"text":169,"type":154,"level":31},"b8","Who this is for",{"id":171,"html":172,"type":137},"b9","\u003Cp>You&#39;ve written a NestJS service or two, used \u003Ccode>@Injectable()\u003C\u002Fcode> and constructor injection, and you&#39;ve run \u003Ccode>npm test\u003C\u002Fcode> on a generated Nest project at least once. If you haven&#39;t yet read the \u003Ca href=\"https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fnestjs-dependency-injection-explained-with-cheat-sheet-4bem\">NestJS dependency injection\u003C\u002Fa> episode of this series, the scope material here (\u003Ccode>DEFAULT\u003C\u002Fcode> vs \u003Ccode>REQUEST\u003C\u002Fcode> vs \u003Ccode>TRANSIENT\u003C\u002Fcode>) will make more sense with that as background — but this article stands on its own.\u003C\u002Fp>",{"id":174,"html":175,"text":175,"type":154,"level":31},"b10","Table of contents",{"id":177,"type":160,"items":178,"ordered":18},"b11",[179,180,181,182,183,184,185,186,187,188,189,190],"\u003Ca href=\"#the-problem-mocking-a-nestjs-service-the-naive-way\">The problem: mocking a NestJS service the naive way\u003C\u002Fa>","\u003Ca href=\"#the-mental-model-the-testing-module-is-a-real-container\">The mental model: the testing module is a real container\u003C\u002Fa>","\u003Ca href=\"#stage-1-overrideprovider-the-smallest-correct-example\">Stage 1: overrideProvider, the smallest correct example\u003C\u002Fa>","\u003Ca href=\"#stage-2-usefactory-when-the-mock-needs-its-own-dependencies\">Stage 2: useFactory, when the mock needs its own dependencies\u003C\u002Fa>","\u003Ca href=\"#stage-3-usemocker-auto-mocking-the-rest-of-the-graph\">Stage 3: useMocker, auto-mocking the rest of the graph\u003C\u002Fa>","\u003Ca href=\"#stage-4-get-vs-resolve-the-scoped-provider-gotcha\">Stage 4: get() vs resolve(), the scoped-provider gotcha\u003C\u002Fa>","\u003Ca href=\"#stage-5-overriding-guards-pipes-interceptors-and-filters\">Stage 5: overriding guards, pipes, interceptors, and filters\u003C\u002Fa>","\u003Ca href=\"#edge-cases-and-gotchas\">Edge cases and gotchas\u003C\u002Fa>","\u003Ca href=\"#best-practices\">Best practices\u003C\u002Fa>","\u003Ca href=\"#faq\">FAQ\u003C\u002Fa>","\u003Ca href=\"#cheat-sheet\">Cheat sheet\u003C\u002Fa>","\u003Ca href=\"#key-takeaways\">Key takeaways\u003C\u002Fa>",{"id":192,"html":193,"text":193,"type":154,"level":31},"b12","The problem: mocking a NestJS service the naive way",{"id":195,"html":196,"type":137},"b13","\u003Cp>Say \u003Ccode>OrdersService\u003C\u002Fcode> depends on \u003Ccode>CacheService\u003C\u002Fcode>:\u003C\u002Fp>",{"id":198,"code":199,"type":141,"language":200,"highlight":201},"b14","@Injectable()\nexport class OrdersService {\n  constructor(private readonly cache: CacheService) {}\n\n  async getOrder(id: string) {\n    const cached = await this.cache.get(id);\n    if (cached) return cached;\n    \u002F\u002F ...load from DB, then this.cache.set(id, order)\n  }\n}","ts",[],{"id":203,"html":204,"type":137},"b15","\u003Cp>The instinct, coming from plain Node testing, is to skip Nest entirely:\u003C\u002Fp>",{"id":206,"code":207,"type":141,"language":200,"highlight":208},"b16","\u002F\u002F The wrong way — bypasses Nest's DI graph completely\nconst fakeCache = { get: jest.fn(), set: jest.fn() };\nconst service = new OrdersService(fakeCache as any);",[],{"id":210,"html":211,"type":137},"b17","\u003Cp>This &quot;works&quot; for exactly this constructor. It breaks the moment someone adds a second constructor parameter — every call site of \u003Ccode>new OrdersService(...)\u003C\u002Fcode> in every test file needs updating by hand, because you&#39;re testing against the \u003Cem>implementation\u003C\u002Fem> of the constructor, not the \u003Cem>contract\u003C\u002Fem> Nest resolves it through. It also can&#39;t test anything that depends on Nest actually wiring the object graph: a guard reading a provider, a module boundary, an \u003Ccode>onModuleInit\u003C\u002Fcode> hook. You&#39;ve tested a plain JavaScript class, not a NestJS provider.\u003C\u002Fp>",{"id":213,"html":214,"type":137},"b18","\u003Cp>The second instinct is \u003Ccode>jest.mock(&#39;.\u002Fcache.service&#39;)\u003C\u002Fcode>, patching the module import. This fights Nest&#39;s instantiation instead of cooperating with it — it works until the service is constructed through \u003Ccode>useFactory\u003C\u002Fcode> or aliased with \u003Ccode>useExisting\u003C\u002Fcode>, at which point the mock and the real DI graph disagree about what \u003Ccode>CacheService\u003C\u002Fcode> even resolves to, and the failure mode is confusing rather than obvious.\u003C\u002Fp>",{"id":216,"html":217,"type":137},"b19","\u003Cp>Nest ships a purpose-built answer to both problems: \u003Ccode>@nestjs\u002Ftesting\u003C\u002Fcode>.\u003C\u002Fp>",{"id":219,"html":220,"text":220,"type":154,"level":31},"b20","The mental model: the testing module is a real container",{"id":222,"html":223,"type":137},"b21","\u003Cp>\u003Cstrong>The mental model:\u003C\u002Fstrong> \u003Ccode>Test.createTestingModule({...})\u003C\u002Fcode> doesn&#39;t create a fake, lightweight stand-in for your app — it builds the \u003Cem>exact same kind of DI container\u003C\u002Fem> Nest builds when your app boots, from the same module metadata. \u003Ccode>overrideProvider(Token)\u003C\u002Fcode> doesn&#39;t monkey-patch anything after the fact; it swaps what \u003Ccode>Token\u003C\u002Fcode> resolves to \u003Cstrong>before\u003C\u002Fstrong> \u003Ccode>.compile()\u003C\u002Fcode> runs, so when the container is built, every real consumer that constructor-injects that token gets your replacement — automatically, through the same resolution path as production.\u003C\u002Fp>",{"id":225,"html":226,"type":137},"b22","\u003Cp>That&#39;s the whole idea in one sentence: \u003Cstrong>override the registration, then let the real container do the wiring.\u003C\u002Fstrong> You never hand-assemble the object graph yourself, so a test can&#39;t quietly drift from how the app is actually wired.\u003C\u002Fp>",{"id":228,"code":229,"type":141,"language":200,"highlight":230},"b23","const moduleRef = await Test.createTestingModule({\n  providers: [OrdersService, CacheService],\n})\n  .overrideProvider(CacheService)\n  .useValue(fakeCache)\n  .compile();\n\nconst service = moduleRef.get(OrdersService);\n\u002F\u002F service.cache is `fakeCache` — Nest injected it, you didn't.",[],{"id":232,"html":233,"type":137},"b24","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> \u003Ccode>OrdersService\u003C\u002Fcode> never mentions the mock. It still says \u003Ccode>constructor(private readonly cache: CacheService)\u003C\u002Fcode>. The override happens one layer up, in how the token is registered — which is exactly why it survives constructor changes that don&#39;t touch \u003Ccode>CacheService\u003C\u002Fcode>&#39;s shape.\u003C\u002Fp>",{"id":235,"html":236,"text":236,"type":154,"level":31},"b25","Stage 1: overrideProvider, the smallest correct example",{"id":238,"html":239,"type":137},"b26","\u003Cp>The three replacement methods mirror the ones you already know from provider registration itself:\u003C\u002Fp>",{"id":241,"code":242,"type":141,"language":200,"highlight":243},"b27","Test.createTestingModule({ providers: [OrdersService, CacheService] })\n  .overrideProvider(CacheService)\n  .useValue(fakeCache)          \u002F\u002F a plain object\u002Fmock — no DI, no lifecycle\n  .compile();\n\nTest.createTestingModule({ providers: [OrdersService, CacheService] })\n  .overrideProvider(CacheService)\n  .useClass(InMemoryCacheService) \u002F\u002F a real class, its own constructor DI-resolved\n  .compile();",[],{"id":245,"html":246,"type":137},"b28","\u003Cp>\u003Ccode>useValue\u003C\u002Fcode> is the right default: fast, explicit, and you can inspect \u003Ccode>fakeCache.get.mock.calls\u003C\u002Fcode> directly. Reach for \u003Ccode>useClass\u003C\u002Fcode> when the fake needs real behavior (an in-memory cache that actually stores and evicts), because unlike \u003Ccode>useValue\u003C\u002Fcode>, \u003Ccode>useClass\u003C\u002Fcode> still goes through Nest&#39;s own instantiation — its constructor can inject other test doubles too.\u003C\u002Fp>",{"id":248,"html":249,"text":249,"type":154,"level":31},"b29","Stage 2: useFactory, when the mock needs its own dependencies",{"id":251,"html":252,"type":137},"b30","\u003Cp>Sometimes the fake itself needs something resolved from the container — a config value, another mock, the module ref:\u003C\u002Fp>",{"id":254,"code":255,"type":141,"language":200,"highlight":256},"b31","Test.createTestingModule({ providers: [OrdersService, CacheService, ConfigService] })\n  .overrideProvider(CacheService)\n  .useFactory({\n    factory: (config: ConfigService) => new FakeCacheWithTtl(config.get('CACHE_TTL')),\n    inject: [ConfigService],\n  })\n  .compile();",[],{"id":258,"html":259,"type":137},"b32","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> \u003Ccode>inject\u003C\u002Fcode> is resolved from the \u003Cem>same\u003C\u002Fem> container being built — including other overrides. This is the one case where the mock still participates in DI, rather than being a static value dropped in from outside.\u003C\u002Fp>",{"id":261,"html":262,"text":262,"type":154,"level":31},"b33","Stage 3: useMocker, auto-mocking the rest of the graph",{"id":264,"html":265,"type":137},"b34","\u003Cp>A module with six providers and you only care about testing one of them means five hand-written \u003Ccode>useValue\u003C\u002Fcode> mocks that add nothing to the assertions. \u003Ccode>useMocker\u003C\u002Fcode> removes that boilerplate: pass it a factory, and Nest calls it for every provider you \u003Cem>didn&#39;t\u003C\u002Fem> explicitly override, generating a jest auto-mock (every method replaced with \u003Ccode>jest.fn()\u003C\u002Fcode>) by default:\u003C\u002Fp>",{"id":267,"code":268,"type":141,"language":200,"highlight":269},"b35","const moduleRef = await Test.createTestingModule({\n  providers: [OrdersService, CacheService, AuditLogService, MetricsService],\n})\n  .useMocker((token) => {\n    if (token === CacheService) return fakeCache; \u002F\u002F your real, meaningful mock\n    if (typeof token === 'function') return jest.fn(); \u002F\u002F auto-mock the rest\n  })\n  .compile();",[],{"id":271,"html":272,"type":137},"b36","\u003Cp>This is genuinely useful for modules with a lot of incidental dependencies. It is also easy to overuse: if \u003Ccode>useMocker\u003C\u002Fcode> quietly auto-mocks the one provider your test is actually supposed to exercise, the test passes for the wrong reason — it never called anything real. Use it to remove noise around the thing you&#39;re testing, never to remove the thing you&#39;re testing.\u003C\u002Fp>",{"id":274,"html":275,"text":275,"type":154,"level":31},"b37","Stage 4: get() vs resolve(), the scoped-provider gotcha",{"id":277,"html":278,"type":137},"b38","\u003Cp>This is the error from the opening of this article, and it isn&#39;t a bug — it&#39;s \u003Ccode>Scope\u003C\u002Fcode> doing exactly what it&#39;s documented to do.\u003C\u002Fp>",{"id":280,"html":281,"type":137},"b39","\u003Cp>\u003Cstrong>\u003Ccode>module.get(Token)\u003C\u002Fcode>\u003C\u002Fstrong> reaches into the container&#39;s static registrations — the ones built once at \u003Ccode>.compile()\u003C\u002Fcode> time. That&#39;s every \u003Ccode>DEFAULT\u003C\u002Fcode>-scoped (singleton) provider. \u003Cstrong>\u003Ccode>module.resolve(Token)\u003C\u002Fcode>\u003C\u002Fstrong> is asynchronous, and creates (or looks up) a \u003Cem>request-scoped sub-tree\u003C\u002Fem> — which is the only kind of container a \u003Ccode>REQUEST\u003C\u002Fcode>- or \u003Ccode>TRANSIENT\u003C\u002Fcode>-scoped provider can live in.\u003C\u002Fp>",{"id":283,"code":284,"type":141,"language":200,"highlight":285},"b40","@Injectable({ scope: Scope.REQUEST })\nexport class AuditLogService { \u002F* ... *\u002F }\n\n\u002F\u002F Throws — a REQUEST-scoped provider has no single static instance to hand back\nmoduleRef.get(AuditLogService);\n\n\u002F\u002F Correct — resolves a fresh instance for this call\nconst auditLog = await moduleRef.resolve(AuditLogService);",[],{"id":287,"html":288,"type":137},"b41","\u003Cp>Two details make this behave differently from \u003Ccode>get()\u003C\u002Fcode> in ways that trip people up:\u003C\u002Fp>",{"id":290,"type":160,"items":291,"ordered":18},"b42",[292,293],"\u003Cstrong>It&#39;s a Promise.\u003C\u002Fstrong> Forgetting \u003Ccode>await\u003C\u002Fcode> gives you a \u003Ccode>Promise&lt;AuditLogService&gt;\u003C\u002Fcode> that happily passes typechecking in a loosely-typed test and fails at runtime in a confusing spot.","\u003Cstrong>Every call gets a new instance, by default.\u003C\u002Fstrong> Two \u003Ccode>moduleRef.resolve(AuditLogService)\u003C\u002Fcode> calls in the same test return two different objects — because in production, two different HTTP requests would too. If you need the \u003Cem>same\u003C\u002Fem> instance twice (to simulate one request touching the provider from two places), pass the same context id explicitly: \u003Ccode>ContextIdFactory.create()\u003C\u002Fcode> once, then \u003Ccode>moduleRef.resolve(Token, contextId)\u003C\u002Fcode> for each call that should share it.",{"id":295,"html":296,"type":137},"b43","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> \u003Ccode>get()\u003C\u002Fcode> vs \u003Ccode>resolve()\u003C\u002Fcode> isn&#39;t a style choice — it&#39;s \u003Ccode>Scope.DEFAULT\u003C\u002Fcode> vs everything else, the exact distinction from how NestJS resolves providers in production. A test that gets this right is also proof the provider&#39;s scope is doing what you think it&#39;s doing.\u003C\u002Fp>",{"id":298,"html":299,"text":299,"type":154,"level":31},"b44","Stage 5: overriding guards, pipes, interceptors, and filters",{"id":301,"html":302,"type":137},"b45","\u003Cp>Everything so far overrides a \u003Cstrong>provider\u003C\u002Fstrong> — something resolved through constructor injection. Guards, pipes, interceptors, and filters applied with \u003Ccode>@UseGuards()\u003C\u002Fcode>, \u003Ccode>@UsePipes()\u003C\u002Fcode>, \u003Ccode>@UseInterceptors()\u003C\u002Fcode>, and \u003Ccode>@UseFilters()\u003C\u002Fcode> (or registered globally) are a separate concern in Nest&#39;s request pipeline, so \u003Ccode>TestingModuleBuilder\u003C\u002Fcode> ships parallel methods for them, with the identical \u003Ccode>useValue\u003C\u002Fcode>\u002F\u003Ccode>useClass\u003C\u002Fcode>\u002F\u003Ccode>useFactory\u003C\u002Fcode> chain:\u003C\u002Fp>",{"id":304,"code":305,"type":141,"language":200,"highlight":306},"b46","const moduleRef = await Test.createTestingModule({\n  controllers: [OrdersController],\n})\n  .overrideGuard(RolesGuard)\n  .useValue({ canActivate: () => true }) \u002F\u002F always allow, for this test\n  .compile();\n\nconst app = moduleRef.createNestApplication();\nawait app.init();\n\u002F\u002F now use supertest against `app.getHttpServer()` for a real e2e request",[],{"id":308,"html":309,"type":137},"b47","\u003Cp>This is the tool for an \u003Cstrong>end-to-end\u003C\u002Fstrong> test — one that goes through \u003Ccode>createNestApplication()\u003C\u002Fcode> and an actual HTTP request via \u003Ccode>supertest\u003C\u002Fcode>, exercising the real request lifecycle (middleware → guards → interceptors → pipes → handler → interceptors → filters, covered in \u003Ca href=\"https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fnestjs-request-lifecycle-explained-with-cheat-sheet-227l\">the request lifecycle episode\u003C\u002Fa> of this series) with just the one guard swapped out. It is the difference between testing &quot;does \u003Ccode>OrdersService.getOrder()\u003C\u002Fcode> return the right shape&quot; and &quot;does a real request to \u003Ccode>GET \u002Forders\u002F:id\u003C\u002Fcode> actually reach the handler.&quot;\u003C\u002Fp>",{"id":311,"html":312,"text":312,"type":154,"level":31},"b48","Edge cases and gotchas",{"id":314,"type":160,"items":315,"ordered":18},"b49",[316,317,318,319,320],"\u003Cstrong>Overrides must be declared before \u003Ccode>.compile()\u003C\u002Fcode>.\u003C\u002Fstrong> There is no &quot;override after the fact&quot; — the testing module is immutable once built, same as the real app&#39;s container.","\u003Cstrong>Overriding module boundaries doesn&#39;t undo \u003Ccode>exports\u003C\u002Fcode>.\u003C\u002Fstrong> If \u003Ccode>CacheService\u003C\u002Fcode> isn&#39;t exported from the module that owns it, importing that module into your test setup still won&#39;t expose it — the same encapsulation rules from a real app apply inside a test.","\u003Cstrong>\u003Ccode>{ strict: false }\u003C\u002Fcode> on \u003Ccode>get()\u003C\u002Fcode> widens the \u003Cem>search\u003C\u002Fem>, not the \u003Cem>scope\u003C\u002Fem>.\u003C\u002Fstrong> \u003Ccode>moduleRef.get(Token, { strict: false })\u003C\u002Fcode> lets you fetch a provider that lives in a different module in the graph than the one you passed to \u003Ccode>createTestingModule\u003C\u002Fcode> — it does not let \u003Ccode>get()\u003C\u002Fcode> reach a scoped provider. That still needs \u003Ccode>resolve()\u003C\u002Fcode>.","\u003Cstrong>Lifecycle hooks still run.\u003C\u002Fstrong> \u003Ccode>onModuleInit\u003C\u002Fcode>, \u003Ccode>onApplicationBootstrap\u003C\u002Fcode>, and friends fire for real during \u003Ccode>.compile()\u003C\u002Fcode> (and \u003Ccode>app.init()\u003C\u002Fcode> for a full app) exactly as they would in production. A provider that opens a real connection in \u003Ccode>onModuleInit\u003C\u002Fcode> will try to open one in your test too, unless it&#39;s the thing you overrode.","\u003Cstrong>\u003Ccode>overrideModule\u003C\u002Fcode> swaps a whole module, not a token.\u003C\u002Fstrong> It&#39;s the heaviest override available — useful for replacing an entire \u003Ccode>DatabaseModule\u003C\u002Fcode> with an in-memory test double — and correspondingly rare; reach for \u003Ccode>overrideProvider\u003C\u002Fcode> first.",{"id":322,"html":323,"text":323,"type":154,"level":31},"b50","Best practices",{"id":325,"type":160,"items":326,"ordered":18},"b51",[327,328,329,330,331],"\u003Cstrong>Override the narrowest token that makes the test honest.\u003C\u002Fstrong> Swap \u003Ccode>CacheService\u003C\u002Fcode>, not \u003Ccode>OrdersModule\u003C\u002Fcode> — the more of the real graph you keep, the more the test proves.","\u003Cstrong>Prefer real modules plus \u003Ccode>overrideProvider\u003C\u002Fcode> over hand-built instances.\u003C\u002Fstrong> Let Nest do the constructor wiring; don&#39;t reimplement it in a test helper that can drift from the real module.","\u003Cstrong>Reach for \u003Ccode>useMocker\u003C\u002Fcode> to cut noise, not to cut assertions.\u003C\u002Fstrong> If a test can pass without the &quot;real&quot; mock ever being asked to do anything, that mock probably shouldn&#39;t be auto-generated.","\u003Cstrong>Match the real provider&#39;s scope in the fake.\u003C\u002Fstrong> A \u003Ccode>useValue\u003C\u002Fcode> mock standing in for a \u003Ccode>REQUEST\u003C\u002Fcode>-scoped provider is fine for a unit test that doesn&#39;t care about per-request identity — but if the article&#39;s \u003Ccode>resolve()\u003C\u002Fcode> distinction matters to what you&#39;re testing, keep the scope on the override too.","\u003Cstrong>Use \u003Ccode>createNestApplication()\u003C\u002Fcode> sparingly.\u003C\u002Fstrong> It&#39;s the right tool for a genuine end-to-end test of routing, guards, and the lifecycle — not the default for testing one service&#39;s logic.",{"id":333,"html":334,"text":334,"type":154,"level":31},"b52","FAQ",{"id":336,"html":337,"text":338,"type":154,"level":43},"b53","Why does \u003Ccode>moduleRef.get()\u003C\u002Fcode> throw for a provider I know is registered?","Why does moduleRef.get() throw for a provider I know is registered?",{"id":340,"html":341,"type":137},"b54","\u003Cp>Because it&#39;s \u003Ccode>DEFAULT\u003C\u002Fcode>-scoped only. \u003Ccode>get()\u003C\u002Fcode> reads the container&#39;s static registrations, built once at \u003Ccode>.compile()\u003C\u002Fcode>. A \u003Ccode>REQUEST\u003C\u002Fcode>- or \u003Ccode>TRANSIENT\u003C\u002Fcode>-scoped provider has no single static instance — use \u003Ccode>moduleRef.resolve()\u003C\u002Fcode> instead, and \u003Ccode>await\u003C\u002Fcode> it.\u003C\u002Fp>",{"id":343,"html":344,"text":344,"type":154,"level":43},"b55","Can I call overrideProvider() after compile()?",{"id":346,"html":347,"type":137},"b56","\u003Cp>No. Every override method has to be chained onto the builder before \u003Ccode>.compile()\u003C\u002Fcode> runs; the compiled \u003Ccode>TestingModule\u003C\u002Fcode> is a finished container.\u003C\u002Fp>",{"id":349,"html":350,"text":350,"type":154,"level":43},"b57","Is overrideProvider the same as jest.mock()?",{"id":352,"html":353,"type":137},"b58","\u003Cp>No, and that&#39;s the point. \u003Ccode>jest.mock()\u003C\u002Fcode> patches a module import at the file-system\u002Frequire level, outside Nest&#39;s awareness. \u003Ccode>overrideProvider()\u003C\u002Fcode> changes what a token resolves to \u003Cem>inside Nest&#39;s own DI graph\u003C\u002Fem>, so every real consumer that constructor-injects it gets the override through the identical resolution path production uses.\u003C\u002Fp>",{"id":355,"html":356,"text":356,"type":154,"level":43},"b59","Does useMocker replace the need for overrideProvider?",{"id":358,"html":359,"type":137},"b60","\u003Cp>No — they compose. Use explicit \u003Ccode>overrideProvider().useValue()\u003C\u002Fcode> for the providers your assertions actually check, and let \u003Ccode>useMocker\u003C\u002Fcode> fill in everything else so you&#39;re not hand-writing mocks for providers the test doesn&#39;t care about.\u003C\u002Fp>",{"id":361,"html":362,"text":362,"type":154,"level":43},"b61","Do I need createNestApplication() for a unit test?",{"id":364,"html":365,"type":137},"b62","\u003Cp>No. \u003Ccode>moduleRef.get()\u003C\u002Fcode>\u002F\u003Ccode>.resolve()\u003C\u002Fcode> against the compiled \u003Ccode>TestingModule\u003C\u002Fcode> is enough for testing a single provider&#39;s logic. \u003Ccode>createNestApplication()\u003C\u002Fcode> is for when the test needs the real HTTP request lifecycle — routing, guards, pipes, filters — not just a provider&#39;s method.\u003C\u002Fp>",{"id":367,"html":368,"type":137},"b63","\u003C!-- playground:start -->",{"id":370,"html":371,"text":371,"type":154,"level":31},"b64","🎮 Try it yourself",{"id":373,"html":374,"type":137},"b65","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnestjs-weekly-testing-provider-overrides\u002Fplayground\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":376,"html":377,"type":137},"b66","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":379,"html":380,"type":137},"b67","\u003C!-- playground:end -->",{"id":382,"html":383,"text":383,"type":154,"level":31},"b68","Cheat sheet",{"id":385,"head":386,"rows":390,"type":431},"b69",[387,388,389],"Task","Code","Notes",[391,395,399,403,407,411,415,419,423,427],[392,393,394],"Build a test module","\u003Ccode>await Test.createTestingModule({ providers, imports, controllers }).compile()\u003C\u002Fcode>","Same DI graph shape as a real app module",[396,397,398],"Replace a value\u002Fmock","\u003Ccode>.overrideProvider(Token).useValue(mock)\u003C\u002Fcode>","No DI on the mock itself",[400,401,402],"Replace with a real fake class","\u003Ccode>.overrideProvider(Token).useClass(FakeImpl)\u003C\u002Fcode>","\u003Ccode>FakeImpl\u003C\u002Fcode>&#39;s own constructor is still DI-resolved",[404,405,406],"Replace with a computed value","\u003Ccode>.overrideProvider(Token).useFactory({ factory, inject })\u003C\u002Fcode>","\u003Ccode>inject\u003C\u002Fcode> resolves from the same container",[408,409,410],"Auto-mock everything else","\u003Ccode>.useMocker((token) =&gt; ...)\u003C\u002Fcode>","Return \u003Ccode>undefined\u003C\u002Fcode> to let Nest auto-mock; explicit mocks for what you assert on",[412,413,414],"Get a \u003Ccode>DEFAULT\u003C\u002Fcode>-scoped provider","\u003Ccode>moduleRef.get(Token)\u003C\u002Fcode>","Throws for \u003Ccode>REQUEST\u003C\u002Fcode>\u002F\u003Ccode>TRANSIENT\u003C\u002Fcode> scope",[416,417,418],"Get a scoped provider","\u003Ccode>await moduleRef.resolve(Token)\u003C\u002Fcode>","Async; new instance per call unless given the same \u003Ccode>contextId\u003C\u002Fcode>",[420,421,422],"Search outside the local module","\u003Ccode>moduleRef.get(Token, { strict: false })\u003C\u002Fcode>","Widens \u003Cem>search\u003C\u002Fem>, not scope — still can&#39;t fetch a scoped provider",[424,425,426],"Override a guard\u002Fpipe\u002Finterceptor\u002Ffilter","\u003Ccode>.overrideGuard(G).useValue({ canActivate: () =&gt; true })\u003C\u002Fcode>","Same chain as \u003Ccode>overrideProvider\u003C\u002Fcode>; needs \u003Ccode>createNestApplication()\u003C\u002Fcode> for e2e",[428,429,430],"Swap an entire module","\u003Ccode>.overrideModule(RealModule).useModule(FakeModule)\u003C\u002Fcode>","Heaviest option — use rarely","table",{"id":433,"html":434,"type":137},"b70","\u003C!-- quiz:start -->",{"id":436,"html":437,"text":437,"type":154,"level":31},"b71","🧠 Test yourself",{"id":439,"html":440,"type":137},"b72","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnestjs-weekly-testing-provider-overrides\u002Fquiz\">Take the 10-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":442,"html":443,"type":137},"b73","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":445,"html":446,"type":137},"b74","\u003C!-- quiz:end -->",{"id":448,"html":449,"text":449,"type":154,"level":31},"b75","Key takeaways",{"id":451,"type":160,"items":452,"ordered":18},"b76",[453,454,455,456,457],"\u003Ccode>Test.createTestingModule\u003C\u002Fcode> compiles a \u003Cstrong>real\u003C\u002Fstrong> DI container from the same module metadata your app boots with — it isn&#39;t a mock registry.","\u003Ccode>overrideProvider\u003C\u002Fcode> swaps a token&#39;s registration \u003Cstrong>before\u003C\u002Fstrong> compilation, so every real consumer still gets wired to it through normal constructor injection.","\u003Ccode>module.get()\u003C\u002Fcode> only reaches \u003Ccode>DEFAULT\u003C\u002Fcode>-scoped providers; \u003Ccode>REQUEST\u003C\u002Fcode> and \u003Ccode>TRANSIENT\u003C\u002Fcode> scope require \u003Ccode>await module.resolve()\u003C\u002Fcode> — a different method, not an option flag.","\u003Ccode>useMocker\u003C\u002Fcode> auto-generates mocks for providers you didn&#39;t explicitly override — a time-saver that can also hide a test that never exercises anything real.","Guards, pipes, interceptors, and filters get their own override methods, aimed at end-to-end tests through \u003Ccode>createNestApplication()\u003C\u002Fcode>, not at constructor-injected providers.",{"id":459,"html":460,"type":137},"b77","\u003Cp>That confusing &quot;use resolve() instead of get()&quot; error from the opening isn&#39;t NestJS being finicky — it&#39;s the same \u003Ccode>Scope\u003C\u002Fcode> contract from the DI container showing up at test time instead of in production, which is exactly where you&#39;d rather find it. Now when a test throws that error, you&#39;ll know precisely which of the two questions it&#39;s actually asking: is this provider a singleton, or isn&#39;t it?\u003C\u002Fp>",{"id":462,"html":463,"type":137},"b78","\u003Cp>What&#39;s the trickiest NestJS testing bug you&#39;ve run into — a scope mismatch, a missing override, something else entirely? Drop it in the comments.\u003C\u002Fp>",{"id":465,"type":466},"b79","divider",{"id":468,"html":469,"type":137},"b80","\u003Cp>🚀 \u003Cstrong>Want more like this?\u003C\u002Fstrong> Every guide, playground, and quiz lives on \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">bestpractic.org\u003C\u002Fa>\u003C\u002Fstrong> — open it and \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">sign up free\u003C\u002Fa>\u003C\u002Fstrong> so the next one finds you.\u003C\u002Fp>",{"id":471,"html":472,"type":137},"b81","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":474,"type":160,"items":475,"ordered":18},"b82",[476,477,478],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","A unit test for `OrdersController` calls `Test.createTestingModule({...}).compile()`, then `moduleRef.get(AuditLogService)` to grab the mock and assert it was called. Every other provider in the test resolves without a hitch. This one throws:\n\n```\nError: AuditLogService is a scoped provider. Use \"moduleRef.resolve()\" instead of \"moduleRef.get()\".\n```\n\nNothing about the test setup looks wrong. `AuditLogService` is in the `providers` array. It was overridden with `useValue`. The override even worked — the controller under test is using the mock, not the real thing. The one line that's wrong is the *retrieval* method, and until you understand what `get()` and `resolve()` are each actually reaching into, the fix looks like superstition: \"just switch to `resolve()` and it works,\" without knowing why.\n\nThis is written against **NestJS 12.0.x** (verified September 2026, current `@nestjs\u002Fcore` release). Everything here — `Test.createTestingModule`, the override methods, `get()`\u002F`resolve()` — has been stable across the v9–v12 line; nothing in this article depends on a v12-specific change.\n\n## What you'll learn\n\nBy the end of this article you'll be able to:\n\n- Explain what a NestJS testing module actually *is* — not a mock registry, a real DI container\n- Use `overrideProvider().useValue()\u002F.useClass()\u002F.useFactory()` to swap a dependency without breaking the rest of the module's wiring\n- Know exactly when `module.get()` works and when it throws, and reach for `module.resolve()` correctly when it does\n- Use `useMocker` to stop hand-writing boilerplate mocks — and know when *not* to\n- Override a guard, pipe, interceptor, or filter for an end-to-end test, not just a constructor-injected provider\n\n## Who this is for\n\nYou've written a NestJS service or two, used `@Injectable()` and constructor injection, and you've run `npm test` on a generated Nest project at least once. If you haven't yet read the [NestJS dependency injection](https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fnestjs-dependency-injection-explained-with-cheat-sheet-4bem) episode of this series, the scope material here (`DEFAULT` vs `REQUEST` vs `TRANSIENT`) will make more sense with that as background — but this article stands on its own.\n\n## Table of contents\n\n- [The problem: mocking a NestJS service the naive way](#the-problem-mocking-a-nestjs-service-the-naive-way)\n- [The mental model: the testing module is a real container](#the-mental-model-the-testing-module-is-a-real-container)\n- [Stage 1: overrideProvider, the smallest correct example](#stage-1-overrideprovider-the-smallest-correct-example)\n- [Stage 2: useFactory, when the mock needs its own dependencies](#stage-2-usefactory-when-the-mock-needs-its-own-dependencies)\n- [Stage 3: useMocker, auto-mocking the rest of the graph](#stage-3-usemocker-auto-mocking-the-rest-of-the-graph)\n- [Stage 4: get() vs resolve(), the scoped-provider gotcha](#stage-4-get-vs-resolve-the-scoped-provider-gotcha)\n- [Stage 5: overriding guards, pipes, interceptors, and filters](#stage-5-overriding-guards-pipes-interceptors-and-filters)\n- [Edge cases and gotchas](#edge-cases-and-gotchas)\n- [Best practices](#best-practices)\n- [FAQ](#faq)\n- [Cheat sheet](#cheat-sheet)\n- [Key takeaways](#key-takeaways)\n\n## The problem: mocking a NestJS service the naive way\n\nSay `OrdersService` depends on `CacheService`:\n\n```typescript\n@Injectable()\nexport class OrdersService {\n  constructor(private readonly cache: CacheService) {}\n\n  async getOrder(id: string) {\n    const cached = await this.cache.get(id);\n    if (cached) return cached;\n    \u002F\u002F ...load from DB, then this.cache.set(id, order)\n  }\n}\n```\n\nThe instinct, coming from plain Node testing, is to skip Nest entirely:\n\n```typescript\n\u002F\u002F The wrong way — bypasses Nest's DI graph completely\nconst fakeCache = { get: jest.fn(), set: jest.fn() };\nconst service = new OrdersService(fakeCache as any);\n```\n\nThis \"works\" for exactly this constructor. It breaks the moment someone adds a second constructor parameter — every call site of `new OrdersService(...)` in every test file needs updating by hand, because you're testing against the *implementation* of the constructor, not the *contract* Nest resolves it through. It also can't test anything that depends on Nest actually wiring the object graph: a guard reading a provider, a module boundary, an `onModuleInit` hook. You've tested a plain JavaScript class, not a NestJS provider.\n\nThe second instinct is `jest.mock('.\u002Fcache.service')`, patching the module import. This fights Nest's instantiation instead of cooperating with it — it works until the service is constructed through `useFactory` or aliased with `useExisting`, at which point the mock and the real DI graph disagree about what `CacheService` even resolves to, and the failure mode is confusing rather than obvious.\n\nNest ships a purpose-built answer to both problems: `@nestjs\u002Ftesting`.\n\n## The mental model: the testing module is a real container\n\n**The mental model:** `Test.createTestingModule({...})` doesn't create a fake, lightweight stand-in for your app — it builds the *exact same kind of DI container* Nest builds when your app boots, from the same module metadata. `overrideProvider(Token)` doesn't monkey-patch anything after the fact; it swaps what `Token` resolves to **before** `.compile()` runs, so when the container is built, every real consumer that constructor-injects that token gets your replacement — automatically, through the same resolution path as production.\n\nThat's the whole idea in one sentence: **override the registration, then let the real container do the wiring.** You never hand-assemble the object graph yourself, so a test can't quietly drift from how the app is actually wired.\n\n```typescript\nconst moduleRef = await Test.createTestingModule({\n  providers: [OrdersService, CacheService],\n})\n  .overrideProvider(CacheService)\n  .useValue(fakeCache)\n  .compile();\n\nconst service = moduleRef.get(OrdersService);\n\u002F\u002F service.cache is `fakeCache` — Nest injected it, you didn't.\n```\n\n**Key concept:** `OrdersService` never mentions the mock. It still says `constructor(private readonly cache: CacheService)`. The override happens one layer up, in how the token is registered — which is exactly why it survives constructor changes that don't touch `CacheService`'s shape.\n\n## Stage 1: overrideProvider, the smallest correct example\n\nThe three replacement methods mirror the ones you already know from provider registration itself:\n\n```typescript\nTest.createTestingModule({ providers: [OrdersService, CacheService] })\n  .overrideProvider(CacheService)\n  .useValue(fakeCache)          \u002F\u002F a plain object\u002Fmock — no DI, no lifecycle\n  .compile();\n\nTest.createTestingModule({ providers: [OrdersService, CacheService] })\n  .overrideProvider(CacheService)\n  .useClass(InMemoryCacheService) \u002F\u002F a real class, its own constructor DI-resolved\n  .compile();\n```\n\n`useValue` is the right default: fast, explicit, and you can inspect `fakeCache.get.mock.calls` directly. Reach for `useClass` when the fake needs real behavior (an in-memory cache that actually stores and evicts), because unlike `useValue`, `useClass` still goes through Nest's own instantiation — its constructor can inject other test doubles too.\n\n## Stage 2: useFactory, when the mock needs its own dependencies\n\nSometimes the fake itself needs something resolved from the container — a config value, another mock, the module ref:\n\n```typescript\nTest.createTestingModule({ providers: [OrdersService, CacheService, ConfigService] })\n  .overrideProvider(CacheService)\n  .useFactory({\n    factory: (config: ConfigService) => new FakeCacheWithTtl(config.get('CACHE_TTL')),\n    inject: [ConfigService],\n  })\n  .compile();\n```\n\n**Key concept:** `inject` is resolved from the *same* container being built — including other overrides. This is the one case where the mock still participates in DI, rather than being a static value dropped in from outside.\n\n## Stage 3: useMocker, auto-mocking the rest of the graph\n\nA module with six providers and you only care about testing one of them means five hand-written `useValue` mocks that add nothing to the assertions. `useMocker` removes that boilerplate: pass it a factory, and Nest calls it for every provider you *didn't* explicitly override, generating a jest auto-mock (every method replaced with `jest.fn()`) by default:\n\n```typescript\nconst moduleRef = await Test.createTestingModule({\n  providers: [OrdersService, CacheService, AuditLogService, MetricsService],\n})\n  .useMocker((token) => {\n    if (token === CacheService) return fakeCache; \u002F\u002F your real, meaningful mock\n    if (typeof token === 'function') return jest.fn(); \u002F\u002F auto-mock the rest\n  })\n  .compile();\n```\n\nThis is genuinely useful for modules with a lot of incidental dependencies. It is also easy to overuse: if `useMocker` quietly auto-mocks the one provider your test is actually supposed to exercise, the test passes for the wrong reason — it never called anything real. Use it to remove noise around the thing you're testing, never to remove the thing you're testing.\n\n## Stage 4: get() vs resolve(), the scoped-provider gotcha\n\nThis is the error from the opening of this article, and it isn't a bug — it's `Scope` doing exactly what it's documented to do.\n\n**`module.get(Token)`** reaches into the container's static registrations — the ones built once at `.compile()` time. That's every `DEFAULT`-scoped (singleton) provider. **`module.resolve(Token)`** is asynchronous, and creates (or looks up) a *request-scoped sub-tree* — which is the only kind of container a `REQUEST`- or `TRANSIENT`-scoped provider can live in.\n\n```typescript\n@Injectable({ scope: Scope.REQUEST })\nexport class AuditLogService { \u002F* ... *\u002F }\n\n\u002F\u002F Throws — a REQUEST-scoped provider has no single static instance to hand back\nmoduleRef.get(AuditLogService);\n\n\u002F\u002F Correct — resolves a fresh instance for this call\nconst auditLog = await moduleRef.resolve(AuditLogService);\n```\n\nTwo details make this behave differently from `get()` in ways that trip people up:\n\n- **It's a Promise.** Forgetting `await` gives you a `Promise\u003CAuditLogService>` that happily passes typechecking in a loosely-typed test and fails at runtime in a confusing spot.\n- **Every call gets a new instance, by default.** Two `moduleRef.resolve(AuditLogService)` calls in the same test return two different objects — because in production, two different HTTP requests would too. If you need the *same* instance twice (to simulate one request touching the provider from two places), pass the same context id explicitly: `ContextIdFactory.create()` once, then `moduleRef.resolve(Token, contextId)` for each call that should share it.\n\n**Key concept:** `get()` vs `resolve()` isn't a style choice — it's `Scope.DEFAULT` vs everything else, the exact distinction from how NestJS resolves providers in production. A test that gets this right is also proof the provider's scope is doing what you think it's doing.\n\n## Stage 5: overriding guards, pipes, interceptors, and filters\n\nEverything so far overrides a **provider** — something resolved through constructor injection. Guards, pipes, interceptors, and filters applied with `@UseGuards()`, `@UsePipes()`, `@UseInterceptors()`, and `@UseFilters()` (or registered globally) are a separate concern in Nest's request pipeline, so `TestingModuleBuilder` ships parallel methods for them, with the identical `useValue`\u002F`useClass`\u002F`useFactory` chain:\n\n```typescript\nconst moduleRef = await Test.createTestingModule({\n  controllers: [OrdersController],\n})\n  .overrideGuard(RolesGuard)\n  .useValue({ canActivate: () => true }) \u002F\u002F always allow, for this test\n  .compile();\n\nconst app = moduleRef.createNestApplication();\nawait app.init();\n\u002F\u002F now use supertest against `app.getHttpServer()` for a real e2e request\n```\n\nThis is the tool for an **end-to-end** test — one that goes through `createNestApplication()` and an actual HTTP request via `supertest`, exercising the real request lifecycle (middleware → guards → interceptors → pipes → handler → interceptors → filters, covered in [the request lifecycle episode](https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fnestjs-request-lifecycle-explained-with-cheat-sheet-227l) of this series) with just the one guard swapped out. It is the difference between testing \"does `OrdersService.getOrder()` return the right shape\" and \"does a real request to `GET \u002Forders\u002F:id` actually reach the handler.\"\n\n## Edge cases and gotchas\n\n- **Overrides must be declared before `.compile()`.** There is no \"override after the fact\" — the testing module is immutable once built, same as the real app's container.\n- **Overriding module boundaries doesn't undo `exports`.** If `CacheService` isn't exported from the module that owns it, importing that module into your test setup still won't expose it — the same encapsulation rules from a real app apply inside a test.\n- **`{ strict: false }` on `get()` widens the *search*, not the *scope*.** `moduleRef.get(Token, { strict: false })` lets you fetch a provider that lives in a different module in the graph than the one you passed to `createTestingModule` — it does not let `get()` reach a scoped provider. That still needs `resolve()`.\n- **Lifecycle hooks still run.** `onModuleInit`, `onApplicationBootstrap`, and friends fire for real during `.compile()` (and `app.init()` for a full app) exactly as they would in production. A provider that opens a real connection in `onModuleInit` will try to open one in your test too, unless it's the thing you overrode.\n- **`overrideModule` swaps a whole module, not a token.** It's the heaviest override available — useful for replacing an entire `DatabaseModule` with an in-memory test double — and correspondingly rare; reach for `overrideProvider` first.\n\n## Best practices\n\n- **Override the narrowest token that makes the test honest.** Swap `CacheService`, not `OrdersModule` — the more of the real graph you keep, the more the test proves.\n- **Prefer real modules plus `overrideProvider` over hand-built instances.** Let Nest do the constructor wiring; don't reimplement it in a test helper that can drift from the real module.\n- **Reach for `useMocker` to cut noise, not to cut assertions.** If a test can pass without the \"real\" mock ever being asked to do anything, that mock probably shouldn't be auto-generated.\n- **Match the real provider's scope in the fake.** A `useValue` mock standing in for a `REQUEST`-scoped provider is fine for a unit test that doesn't care about per-request identity — but if the article's `resolve()` distinction matters to what you're testing, keep the scope on the override too.\n- **Use `createNestApplication()` sparingly.** It's the right tool for a genuine end-to-end test of routing, guards, and the lifecycle — not the default for testing one service's logic.\n\n## FAQ\n\n### Why does `moduleRef.get()` throw for a provider I know is registered?\n\nBecause it's `DEFAULT`-scoped only. `get()` reads the container's static registrations, built once at `.compile()`. A `REQUEST`- or `TRANSIENT`-scoped provider has no single static instance — use `moduleRef.resolve()` instead, and `await` it.\n\n### Can I call overrideProvider() after compile()?\n\nNo. Every override method has to be chained onto the builder before `.compile()` runs; the compiled `TestingModule` is a finished container.\n\n### Is overrideProvider the same as jest.mock()?\n\nNo, and that's the point. `jest.mock()` patches a module import at the file-system\u002Frequire level, outside Nest's awareness. `overrideProvider()` changes what a token resolves to *inside Nest's own DI graph*, so every real consumer that constructor-injects it gets the override through the identical resolution path production uses.\n\n### Does useMocker replace the need for overrideProvider?\n\nNo — they compose. Use explicit `overrideProvider().useValue()` for the providers your assertions actually check, and let `useMocker` fill in everything else so you're not hand-writing mocks for providers the test doesn't care about.\n\n### Do I need createNestApplication() for a unit test?\n\nNo. `moduleRef.get()`\u002F`.resolve()` against the compiled `TestingModule` is enough for testing a single provider's logic. `createNestApplication()` is for when the test needs the real HTTP request lifecycle — routing, guards, pipes, filters — not just a provider's method.\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnestjs-weekly-testing-provider-overrides\u002Fplayground)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\n## Cheat sheet\n\n| Task | Code | Notes |\n| --- | --- | --- |\n| Build a test module | `await Test.createTestingModule({ providers, imports, controllers }).compile()` | Same DI graph shape as a real app module |\n| Replace a value\u002Fmock | `.overrideProvider(Token).useValue(mock)` | No DI on the mock itself |\n| Replace with a real fake class | `.overrideProvider(Token).useClass(FakeImpl)` | `FakeImpl`'s own constructor is still DI-resolved |\n| Replace with a computed value | `.overrideProvider(Token).useFactory({ factory, inject })` | `inject` resolves from the same container |\n| Auto-mock everything else | `.useMocker((token) => ...)` | Return `undefined` to let Nest auto-mock; explicit mocks for what you assert on |\n| Get a `DEFAULT`-scoped provider | `moduleRef.get(Token)` | Throws for `REQUEST`\u002F`TRANSIENT` scope |\n| Get a scoped provider | `await moduleRef.resolve(Token)` | Async; new instance per call unless given the same `contextId` |\n| Search outside the local module | `moduleRef.get(Token, { strict: false })` | Widens *search*, not scope — still can't fetch a scoped provider |\n| Override a guard\u002Fpipe\u002Finterceptor\u002Ffilter | `.overrideGuard(G).useValue({ canActivate: () => true })` | Same chain as `overrideProvider`; needs `createNestApplication()` for e2e |\n| Swap an entire module | `.overrideModule(RealModule).useModule(FakeModule)` | Heaviest option — use rarely |\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 10-question quiz →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnestjs-weekly-testing-provider-overrides\u002Fquiz)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\n## Key takeaways\n\n- `Test.createTestingModule` compiles a **real** DI container from the same module metadata your app boots with — it isn't a mock registry.\n- `overrideProvider` swaps a token's registration **before** compilation, so every real consumer still gets wired to it through normal constructor injection.\n- `module.get()` only reaches `DEFAULT`-scoped providers; `REQUEST` and `TRANSIENT` scope require `await module.resolve()` — a different method, not an option flag.\n- `useMocker` auto-generates mocks for providers you didn't explicitly override — a time-saver that can also hide a test that never exercises anything real.\n- Guards, pipes, interceptors, and filters get their own override methods, aimed at end-to-end tests through `createNestApplication()`, not at constructor-injected providers.\n\nThat confusing \"use resolve() instead of get()\" error from the opening isn't NestJS being finicky — it's the same `Scope` contract from the DI container showing up at test time instead of in production, which is exactly where you'd rather find it. Now when a test throws that error, you'll know precisely which of the two questions it's actually asking: is this provider a singleton, or isn't it?\n\nWhat's the trickiest NestJS testing bug you've run into — a scope mismatch, a missing override, something else entirely? Drop it in the comments.\n\n---\n\n🚀 **Want more like this?** Every guide, playground, and quiz lives on **[bestpractic.org](https:\u002F\u002Fbestpractic.org\u002F)** — open it and **[sign up free](https:\u002F\u002Fbestpractic.org\u002F)** so the next one finds you.\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)",{"title":108,"canonical":481,"description":109},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fnestjs-weekly-testing-provider-overrides","01a0a630-c856-72d3-bae5-36cc8ebc2527",{"name":484,"part":485,"total":485,"items":486},"NestJS Deep Dive",4,[487,491,496,500],{"slug":488,"title":489,"publishedAt":490,"readingMinutes":111},"nestjs-weekly-request-lifecycle","NestJS Request Lifecycle Explained (with Cheat Sheet)","2026-08-28T17:01:41.941Z",{"slug":492,"title":493,"publishedAt":494,"readingMinutes":495},"nestjs-weekly-dependency-injection-providers-scopes","NestJS Dependency Injection Explained (with Cheat Sheet)","2026-09-13T08:40:18.434Z",14,{"slug":497,"title":498,"publishedAt":499,"readingMinutes":495},"nestjs-weekly-module-encapsulation-exports","NestJS Module Encapsulation Explained (with Cheat Sheet)","2026-09-13T08:40:50.070Z",{"slug":46,"title":108,"publishedAt":112,"readingMinutes":111},{"id":502,"locked":18},"01a0a630-c902-76aa-9cce-cd542c946f7f",[504],{"id":45,"slug":46,"title":48,"_count":505},{"questions":51},[507],{"locale":13,"slug":46},{"id":45,"slug":46,"title":48,"_count":509,"questionCount":51},{"questions":51}]