Skip to Content

Testing

@neststack/config is designed to be easy to test. The key pattern: use envSource to inject fake environment variables and NestStackConfigModule.reset() to clear state between tests.

Setup

Tests use Vitest  with the @nestjs/testing module:

import { describe, it, expect, beforeEach } from 'vitest'; import { Test } from '@nestjs/testing'; import { NestStackConfigModule, ConfigService, defineConfig } from '@neststack/config'; import { z } from 'zod';

Reset Between Tests

The ConfigStore is a static singleton. You must reset it before each test to prevent state leaking between tests:

beforeEach(() => { NestStackConfigModule.reset(); });

Without this, the second test will fail with “Configuration namespace X is already registered.”

Inject Fake Environment Variables

Use the envSource option to provide test values without modifying process.env:

const testConfig = defineConfig({ namespace: 'database', schema: z.object({ host: z.string(), port: z.number().default(5432), }), load: ({ env }) => ({ host: env.getString('DB_HOST'), port: env.getNumber('DB_PORT', 5432), }), }); it('should read config from envSource', async () => { const module = await Test.createTestingModule({ imports: [ NestStackConfigModule.forRoot({ configs: [testConfig], envSource: { DB_HOST: 'test-host', DB_PORT: '9999', }, }), ], }).compile(); const config = module.get(ConfigService); expect(config.get('database.host')).toBe('test-host'); expect(config.get('database.port')).toBe(9999); });

Test Overrides

Use overrides to test specific scenarios without changing environment variables:

it('should apply overrides', async () => { const module = await Test.createTestingModule({ imports: [ NestStackConfigModule.forRoot({ configs: [testConfig], envSource: { DB_HOST: 'original' }, overrides: { database: { host: 'overridden' }, }, }), ], }).compile(); const config = module.get(ConfigService); expect(config.get('database.host')).toBe('overridden'); });

Test Validation Errors

Verify that invalid config is caught:

it('should throw on invalid config', async () => { await expect( Test.createTestingModule({ imports: [ NestStackConfigModule.forRoot({ configs: [testConfig], envSource: {}, // DB_HOST is required }), ], }).compile(), ).rejects.toThrow('Config validation failed'); });

Test Feature Modules

it('should register feature config', async () => { const featureConfig = defineConfig({ namespace: 'feature', schema: z.object({ enabled: z.boolean().default(true) }), }); const module = await Test.createTestingModule({ imports: [ NestStackConfigModule.forRoot({ configs: [] }), NestStackConfigModule.forFeature(featureConfig), ], }).compile(); const config = module.get(ConfigService); expect(config.get('feature.enabled')).toBe(true); });

Test explain()

it('should explain value sources', async () => { const module = await Test.createTestingModule({ imports: [ NestStackConfigModule.forRoot({ configs: [testConfig], envSource: { DB_HOST: 'loaded-host' }, }), ], }).compile(); const config = module.get(ConfigService); const explanation = config.explain('database.host'); expect(explanation.source).toBe('loader'); expect(explanation.isSecret).toBe(false); expect(explanation.value).toBe('loaded-host'); });

Coverage Requirements

The @neststack/config package enforces 100% code coverage on lines, branches, functions, and statements. When adding new features, ensure all code paths are tested:

pnpm nx test config --coverage

The coverage report shows exactly which lines are uncovered.

Last updated on