NestJS/Jest E2E测试中如何覆盖AppModule导入的TypeOrmModule配置?
我正在为API编写E2E测试,创建TestingModule时必须导入AppModule,代码如下:
beforeAll(async () => { const module = await Test.createTestingModule({ imports: [AppModule], }) // ... })
问题在于AppModule中导入了TypeOrmModule.forRootAsync({useFactory: getTypeOrmConfig, dataSourceFactory: getDataSource, }),其中getTypeOrmConfig从环境变量获取配置。但测试中我在Mock服务,不需要连接真实数据库。
我希望找到一种仅针对E2E测试覆盖该模块配置的方法。修改.env文件并不合适,因为集成测试需要连接真实数据库,且我不想在CI中为不同类型的测试下载不同的.env文件。
我尝试过使用overrideModule:
beforeAll(async () => { const module = await Test.createTestingModule({ imports: [AppModule], }) .overrideModule( TypeOrmModule.forRoot({}) || TypeOrmModule || TypeOrmModule.forRootAsync({}) ) .useModule( TypeOrmModule.forRoot({ type: "sqlite", database: "./test/database-mock/db", }), ) //... })
但这不起作用,应用仍尝试连接真实数据库。是否有方法在创建TestModule时覆盖AppModule中导入的模块?我们可以覆盖AppModule中导入模块的提供者,我需要类似的模块覆盖方法。
可行的解决方案
方法1:覆盖TypeOrm的配置提供者
NestJS测试模块支持直接覆盖底层提供者,而非模块本身。因为TypeOrmModule.forRootAsync会注册相关配置提供者,你可以通过覆盖TypeOrmModuleOptions或DataSource来替换配置:
import { DataSource } from 'typeorm'; beforeAll(async () => { const module = await Test.createTestingModule({ imports: [AppModule], }) .overrideProvider('TypeOrmModuleOptions') .useValue({ type: 'sqlite', database: './test/database-mock/db', entities: [/* 你的实体类 */], synchronize: true, // 测试环境可开启自动同步 }) .overrideProvider(DataSource) .useFactory({ factory: (options) => new DataSource(options), inject: ['TypeOrmModuleOptions'], }) .compile(); // 后续测试逻辑 });
如果你的getTypeOrmConfig是自定义提供者令牌,也可以直接覆盖该令牌:
beforeAll(async () => { const module = await Test.createTestingModule({ imports: [AppModule], }) .overrideProvider(getTypeOrmConfig) .useValue({ type: 'sqlite', database: './test/database-mock/db', entities: [/* 实体类 */], synchronize: true, }) .compile(); });
方法2:给AppModule添加测试环境分支
修改AppModule,让它根据环境变量自动切换配置,测试时只需设置特定环境变量即可,无需修改.env:
在AppModule中:
@Module({ imports: [ TypeOrmModule.forRootAsync({ useFactory: async () => { if (process.env.NODE_ENV === 'test') { return { type: 'sqlite', database: './test/database-mock/db', entities: [/* 实体类 */], synchronize: true, }; } return getTypeOrmConfig(); }, }), // 其他导入模块 ], }) export class AppModule {}
然后在package.json的测试脚本中设置环境变量:
{ "scripts": { "test:e2e": "NODE_ENV=test jest --config jest-e2e.json" } }
方法3:使用NestJS 9+的replaceModule方法
如果使用NestJS 9及以上版本,可以用replaceModule直接替换AppModule中的TypeOrm模块实例,注意要精确匹配原模块的调用参数:
beforeAll(async () => { const module = await Test.createTestingModule({ imports: [AppModule], }) .replaceModule( TypeOrmModule.forRootAsync({ useFactory: getTypeOrmConfig, dataSourceFactory: getDataSource }), TypeOrmModule.forRoot({ type: 'sqlite', database: './test/database-mock/db', entities: [/* 实体类 */], synchronize: true, }) ) .compile(); });
内容的提问来源于stack exchange,提问作者DanilMakarov

