NestJS ConfigService and external client provider override precedence in Test.createTestingModule
0 reputation · 13 Jul 2025, 21:53 UTC
0 reputation · 13 Jul 2025, 21:53 UTC
Test a NestJS module that depends on an external HTTP client without loading production credentials into the test process. The client is provided through ConfigModule and ConfigService, and tests should replace only the client boundary with a test double using Test.createTestingModule.
It is unclear whether overriding ConfigService values is sufficient, or whether the client provider token itself must be overridden. Because a client can read configuration in its constructor, a config-only override may capture the wrong value or be ignored entirely. Global ConfigModule registration may also change which module owns the override, and module initialization can fail on missing credentials before the double is installed. Behavior differs between @nestjs/testing minor versions that expose useMocker and those that rely on overrideProvider and useValue.
Should tests override the external client provider token directly, or is overriding ConfigService values reliably equivalent? How should the override target be chosen when ConfigModule is global versus non-global, and what version assumption applies to useMocker versus overrideProvider?
35275 reputation · 13 Jul 2025, 22:28 UTC
When a module creates an external HTTP client during initialization, the client will read ConfigService values in its constructor or factory. If you only override the ConfigService after the client has been instantiated, the client already holds the original configuration and may still attempt real HTTP calls. Therefore, the safest and most reliable approach is to override the client provider token itself with a test double.
onModuleInit.ConfigService values later does not affect the already‑created client.ConfigService is overridden.ConfigModule: The provider is registered in the global registry. You can override it anywhere in the test module with .overrideProvider(ClientToken).useValue(mockClient) and it will replace the global instance.ConfigModule: The client provider is scoped to the module that declares it. In this case, you must target that module in Test.createTestingModule:
Test.createTestingModule({
imports: [YourModule],
providers: [
{ provide: ClientToken, useValue: mockClient },
],
}).overrideProvider(ClientToken).useValue(mockClient);
overrideProvider(...).useValue(...) is the standard method.useMocker is introduced for auto‑mocking but it still requires an explicit overrideProvider for the client if you want to avoid real HTTP calls. useMocker only mocks providers that are not already overridden.
const mockClient = {
get: jest.fn().mockResolvedValue({ data: 'mock' }),
post: jest.fn().mockResolvedValue({ data: 'mock' }),
};
const module = await Test.createTestingModule({
imports: [YourModule],
}).overrideProvider(ClientToken).useValue(mockClient).compile();
const client = module.get(ClientToken);
expect(client).toBe(mockClient);
client.get or client.post will hit the mock, and no real HTTP requests will be made.If the client also uses configuration at runtime (e.g., for retries or headers), you can still override ConfigService values in the test module to control those aspects. However, this is an additional safety net, not a replacement for overriding the client provider.
To refine the recommendation further, could you confirm whether the client provider is defined via a factory (possibly async) or as a class constructor? Knowing this will help ensure the mock matches the expected shape and lifecycle.
Override the external HTTP client provider token directly in your test module. This guarantees that the mock is used regardless of when configuration values are set or whether the ConfigModule is global. Use overrideProvider for NestJS v8 and earlier, and pair it with useMocker in v9+ if you want automatic provider mocking but still need to guard the client explicitly.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.