typescript - چگونه یک سرویس nestjs را با استفاده از ConfigService با مقادیر سفارشی تست کنیم؟


من یک سرویس ایجاد کردهام، و ماژول آن به این شکل است:

launchdarkly.module.ts

@Module({
  providers: [LaunchdarklyService],
  exports: [LaunchdarklyService],
  imports: [ConfigService],
})
export class LaunchdarklyModule {}

(این سرویس/ماژول برای این است که برنامه بتواند از ویژگیهای پرچمگذاری LaunchDarkly استفاده کند)

خوشحالم که پیادهسازی سرویس را نشان دهم اگر بخواهید، اما برای کوتاهتر شدن این سؤال، آن را حذف کردم. نکته مهم این است که این سرویس ConfigService را وارد میکند (که از آن برای به دست آوردن کلید SDK لانچدارکلی استفاده میکند).

اما چگونه میتوانم سرویس Launchdarkly را تست کنم؟ این سرویس یک کلید از ConfigService میخواند، بنابراین من میخواهم تستهایی بنویسم که در آن ConfigService مقادیر مختلفی داشته باشد، اما پس از ساعتها تلاش نمیتوانم بفهمم چگونه ConfigService را در یک تست پیکربندی کنم.

این هم تست:

launchdarkly.service.spec.ts

describe('LaunchdarklyService', () => {
  let service: LaunchdarklyService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [LaunchdarklyService],
      imports: [ConfigModule],
    }).compile();

    service = module.get<LaunchdarklyService>(LaunchdarklyService);
  });

  it("should not create a client if there's no key", async () => {
    // somehow I need ConfigService to have key FOO=undefined for this test
    expect(service.client).toBeUndefined();
  });

  it("should create a client if an SDK key is specified", async () => {
    // For this test ConfigService needs to specify FOO=123
    expect(service.client).toBeDefined();
  });
})

من آماده هرگونه پیشنهاد غیرشخصی هستم، فقط میخواهم برنامهام را با پرچم ویژگی مدیریت کنم!

پاسخ شماره ۱

با فرض اینکه LaunchdarklyService به ConfigService نیاز دارد و این سرویس در سازنده تزریق شده است، میتوانید یک نسخه شبیهسازی شده از ConfigService را با استفاده از یک Custom Provider ارائه دهید تا اطلاعات ورود سفارشی مورد نیاز خود را بازگردانید. برای مثال، یک نسخه شبیهسازی شده برای تست شما میتواند به این شکل باشد

describe('LaunchdarklyService', () => {
  let service: LaunchdarklyService;
  let config: ConfigService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [LaunchdarklyService, {
        provide: ConfigService,
        useValue: {
          get: jest.fn((key: string) => {
            // this is being super extra, in the case that you need multiple keys with the `get` method
            if (key === 'FOO') {
              return 123;
            }
            return null;
          })
        }
      ],
    }).compile();

    service = module.get<LaunchdarklyService>(LaunchdarklyService);
    config = module.get<ConfigService>(ConfigService);
  });

  it("should not create a client if there's no key", async () => {
    // somehow I need ConfigService to have key FOO=undefined for this test
    // we can use jest spies to change the return value of a method
    jest.spyOn(config, 'get').mockReturnedValueOnce(undefined);
    expect(service.client).toBeUndefined();
  });

  it("should create a client if an SDK key is specified", async () => {
    // For this test ConfigService needs to specify FOO=123
    // the pre-configured mock takes care of this case
    expect(service.client).toBeDefined();
  });
})

پاسخ شماره ۲

شما میتوانید از نحو پاکتر با استفاده از متد forFeature که توسط ConfigModule ارائه شده است، استفاده کنید.

این یک تابع غیرهمزمان را میپذیرد که باید یک شیء بازگرداند (در مورد شما، شیء Env شما).

مزیت استفاده از متد forFeature این است که میتوانید یک شیء جزئی ثبت کنید بنابراین نیازی به نگرانی در مورد سایر متغیرها (یا انجام تعداد زیادی if) نخواهید داشت.

beforeEach(async () => {
  const moduleRef = await Test.createTestingModule({
    imports: [
      ConfigModule.forFeature(async () => ({
        ANY_KEY_YOU_WANT: 'Any_Value'
      }))
    ],
    providers: [AnyService]
  }).compile()

  service = moduleRef.get<AnyService>(AnyService)
})