Files
nest/integration/hooks/e2e/on-module-init.spec.ts
2026-02-16 11:03:41 +01:00

159 lines
3.9 KiB
TypeScript

import { Injectable, Module, OnModuleInit } from '@nestjs/common';
import { Test } from '@nestjs/testing';
@Injectable()
class TestInjectable implements OnModuleInit {
onModuleInit = vi.fn();
}
describe('OnModuleInit', () => {
it('should call onModuleInit when application starts', async () => {
const module = await Test.createTestingModule({
providers: [TestInjectable],
}).compile();
const app = module.createNestApplication();
await app.init();
const instance = module.get(TestInjectable);
expect(instance.onModuleInit).toHaveBeenCalled();
await app.close();
});
it('should not throw an error when onModuleInit is null', async () => {
const module = await Test.createTestingModule({
providers: [{ provide: 'TEST', useValue: { onModuleInit: null } }],
}).compile();
const app = module.createNestApplication();
await app.init().then(obj => expect(obj).not.toBeUndefined());
await app.close();
});
it('should not throw an error when onModuleInit is undefined', async () => {
const module = await Test.createTestingModule({
providers: [{ provide: 'TEST', useValue: { onModuleInit: undefined } }],
}).compile();
const app = module.createNestApplication();
await app.init().then(obj => expect(obj).not.toBeUndefined());
await app.close();
});
it('should sort modules by distance (topological sort) - DESC order', async () => {
@Injectable()
class CC implements OnModuleInit {
public field: string;
async onModuleInit() {
this.field = 'c-field';
}
}
@Module({})
class C {
static forRoot() {
return {
module: C,
global: true,
providers: [
{
provide: CC,
useValue: new CC(),
},
],
exports: [CC],
};
}
}
@Injectable()
class BB implements OnModuleInit {
public field: string;
constructor(private cc: CC) {}
async onModuleInit() {
this.field = this.cc.field + '_b-field';
}
}
@Module({
providers: [BB],
exports: [BB],
})
class B {}
@Injectable()
class AA implements OnModuleInit {
public field: string;
constructor(private bb: BB) {}
async onModuleInit() {
this.field = this.bb.field + '_a-field';
}
}
@Module({
imports: [B],
providers: [AA],
})
class A {}
@Module({
imports: [A, C.forRoot()],
})
class AppModule {}
const module = await Test.createTestingModule({
imports: [AppModule],
}).compile();
const app = module.createNestApplication();
await app.init();
const instance = module.get(AA);
expect(instance.field).toBe('c-field_b-field_a-field');
await app.close();
});
it('should sort components within a single module by injection hierarchy - DESC order', async () => {
@Injectable()
class A implements OnModuleInit {
onModuleInit = vi.fn();
}
@Injectable()
class AHost implements OnModuleInit {
constructor(private a: A) {}
onModuleInit = vi.fn();
}
@Injectable()
class Composition implements OnModuleInit {
constructor(
private a: A,
private host: AHost,
) {}
onModuleInit = vi.fn();
}
@Module({
providers: [AHost, A, Composition],
})
class AModule {}
const module = await Test.createTestingModule({
imports: [AModule],
}).compile();
const app = module.createNestApplication();
await app.init();
await app.close();
const child = module.get(A);
const parent = module.get(AHost);
const composition = module.get(Composition);
expect(child.onModuleInit).toHaveBeenCalledBefore(parent.onModuleInit);
expect(parent.onModuleInit).toHaveBeenCalledBefore(
composition.onModuleInit,
);
});
});