Nestjs overrideProvider 与单元测试中的提供者
Nestjs overrideProvider vs provider in unit testing
我看到了两种在 NestJS 中模拟服务进行单元测试的方法,第一种与我们在真实模块中定义提供者相同,例如:
const module = await Test.createTestingModule({
providers: [
UserService,
{
provide: getRepositoryToken(User),
useValue: mockUsersRepository,
}
],
}).compile();
另一种方法是overrideProvider
。如下:
const module = await Test.createTestingModule({
imports: [UserModule]
})
.overrideProvider(getRepositoryToken(User))
.useValue(mockUsersRepository)
.compile();
有什么区别?
区别很简单。
使用第一种方法(提供程序数组),您创建自定义测试模块 来测试(可能)UserService
。
在第二种方法中,您使用完整模块,其形状与在应用程序本身中使用的完全相同。
结果完全一样 - 您的模拟被注入到 UserService
的构造函数中。
第一种方法更适合小型的,主要是单元测试,但这些测试也可以在根本不使用 NestJS 测试工具的情况下完成(只需手动将 mock 传递给 ctor),而第二种方法在集成测试。
存储库不是用来解释的好例子,但想想Logger
。
您正在执行 2 个或更多模块的一些集成测试。
您不想手动创建大型测试模块(这也会破坏与模块真实形状的连接),但您只想导入正在一起测试的模块和 .overrideProvider
for Logger
与例如loggerMock
允许您断言所有测试模块中的所有记录器调用。
示例:
@Module({providers: [LoggerService], exports: [LoggerService]})
export class LoggerModule {}
@Module({imports: [LoggerModule], providers: [FooService]})
export class FooModule {}
@Module({imports: [LoggerModule], providers: [BarService]})
export class BarModule {}
@Module({imports: [FooModule, BarModule]}
export class AppModule {}
// TEST
const testModule = await Test.createTestingModule({
import: [AppModule]
})
.overrideProvider(LoggerService)
.useValue(/* your logger mock will be provided in both FooService and BarService and you can easily test all related to logs then */)
.compile();
我希望这是清楚的。如果没有,请发表评论,我会尽力解释更多。
所以让我尝试这样解释:
overrideProvider
当您导入了整个模块并需要覆盖它作为提供者的某些内容时很有用。一个用例,就像提到的答案一样,将覆盖一个记录器。所以说你有
const modRef = await Test.createTestingModule({
import: [AuthModule]
}).compile();
并假设 AuthModule
有 imports: [ LoggerModule ]
。在我们的测试中,我们真的不想看到所有创建的日志,但我们不能为 LoggerService
提供自定义提供程序,因为它是通过 LoggerModule
导入和使用的(覆盖注入token 并不是一种常见的做法)。因此,要为 LoggerService
提供我们自己的实现(假设我们只需要一个 noop log
方法),我们可以执行以下操作
const modRef = await Test.createTestingModule({
import: [AuthModule]
})
.overrideProvider(LoggerService)
.useValue({ log: () => { /* noop */ } })
.compile();
现在当我们的 AuthService
调用 this.logger.log()
时,它只会调用这个 noop
并完成它。
另一方面,如果我们正在进行单元测试,通常您不需要 overrideProvider
因为您只需直接在测试模块的 provider
和自定义提供程序中设置元数据并使用它。
当你有使用imports
时,overrideProvider
真的很有用(比如集成和e2e测试),否则,一般来说,最好使用自定义提供程序
我看到了两种在 NestJS 中模拟服务进行单元测试的方法,第一种与我们在真实模块中定义提供者相同,例如:
const module = await Test.createTestingModule({
providers: [
UserService,
{
provide: getRepositoryToken(User),
useValue: mockUsersRepository,
}
],
}).compile();
另一种方法是overrideProvider
。如下:
const module = await Test.createTestingModule({
imports: [UserModule]
})
.overrideProvider(getRepositoryToken(User))
.useValue(mockUsersRepository)
.compile();
有什么区别?
区别很简单。
使用第一种方法(提供程序数组),您创建自定义测试模块 来测试(可能)UserService
。
在第二种方法中,您使用完整模块,其形状与在应用程序本身中使用的完全相同。
结果完全一样 - 您的模拟被注入到 UserService
的构造函数中。
第一种方法更适合小型的,主要是单元测试,但这些测试也可以在根本不使用 NestJS 测试工具的情况下完成(只需手动将 mock 传递给 ctor),而第二种方法在集成测试。
存储库不是用来解释的好例子,但想想Logger
。
您正在执行 2 个或更多模块的一些集成测试。
您不想手动创建大型测试模块(这也会破坏与模块真实形状的连接),但您只想导入正在一起测试的模块和 .overrideProvider
for Logger
与例如loggerMock
允许您断言所有测试模块中的所有记录器调用。
示例:
@Module({providers: [LoggerService], exports: [LoggerService]})
export class LoggerModule {}
@Module({imports: [LoggerModule], providers: [FooService]})
export class FooModule {}
@Module({imports: [LoggerModule], providers: [BarService]})
export class BarModule {}
@Module({imports: [FooModule, BarModule]}
export class AppModule {}
// TEST
const testModule = await Test.createTestingModule({
import: [AppModule]
})
.overrideProvider(LoggerService)
.useValue(/* your logger mock will be provided in both FooService and BarService and you can easily test all related to logs then */)
.compile();
我希望这是清楚的。如果没有,请发表评论,我会尽力解释更多。
所以让我尝试这样解释:
overrideProvider
当您导入了整个模块并需要覆盖它作为提供者的某些内容时很有用。一个用例,就像提到的答案一样,将覆盖一个记录器。所以说你有
const modRef = await Test.createTestingModule({
import: [AuthModule]
}).compile();
并假设 AuthModule
有 imports: [ LoggerModule ]
。在我们的测试中,我们真的不想看到所有创建的日志,但我们不能为 LoggerService
提供自定义提供程序,因为它是通过 LoggerModule
导入和使用的(覆盖注入token 并不是一种常见的做法)。因此,要为 LoggerService
提供我们自己的实现(假设我们只需要一个 noop log
方法),我们可以执行以下操作
const modRef = await Test.createTestingModule({
import: [AuthModule]
})
.overrideProvider(LoggerService)
.useValue({ log: () => { /* noop */ } })
.compile();
现在当我们的 AuthService
调用 this.logger.log()
时,它只会调用这个 noop
并完成它。
另一方面,如果我们正在进行单元测试,通常您不需要 overrideProvider
因为您只需直接在测试模块的 provider
和自定义提供程序中设置元数据并使用它。
当你有使用imports
时,overrideProvider
真的很有用(比如集成和e2e测试),否则,一般来说,最好使用自定义提供程序