如何为存储库函数配置NestJs Jest测试

bxfogqkk  于 12个月前  发布在  Jest
关注(0)|答案(1)|浏览(113)

我的目标是为nestjs项目中的自定义存储库文件中的函数添加一些单元测试。

服务:

@Injectable()
export class TacoService {

  constructor(
    public readonly tacoRepository: TacoRepository,
    private readonly userRepository: UserRepository,
    public readonly bcdaRepository: BurritoRepository,
    @Inject(forwardRef(() => MakerService))
    private readonly makerService: MakerService,
    ...
  ) {}
...
}

字符串

仓库:

@Injectable()
export class TacoRepository {

constructor(
  @Inject(Topping.name) private readonly toppingRepository: typeof Topping,
  @Inject(Sauce.name) private readonly sauceRepository: typeof Sauce,
  ...
  ) {}

 public getTacoString(offset: number | undefined, limit: number | undefined) {
    return !isUndefined(offset) && !isUndefined(limit) ? `${limit} ${offset}` : '';
  }
...
}

测试:

describe('TacoRepository test suite', () => {
  let tacoService: TacoService;

  beforeAll(async () => {
    const module = await Test.createTestingModule({
      providers: [TacoService],
      exports: [TacoService],
    })
      .useMocker(token => {
        if (typeof token === 'function') {
            const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
            const Mock = moduleMocker.generateFromMetadata(mockMetadata);
            return new Mock();
        }
      })
      .compile();

    tacoService = module.get<TacoService>(TacoService);
  });

  describe('Get taco string tests', () => {
    let actual;

    it('should return an empty taco string when the limit and/or offset are undefined', () => {
      actual = tacoService.tacoRepository.getTacoString(undefined, undefined);
      expect(actual).toEqual('');
      actual = tacoService.tacoRepository.getTacoString(undefined, 10);
      expect(actual).toEqual('');
      actual = tacoService.tacoRepository.getTacoString(0, undefined);
      expect(actual).toEqual('');
    });
  });
});


当我执行测试时,我总是得到一个“undefined”的实际值,这是因为TacoRepository被mock了。但是我不知道如何不mock这个仓库,然后mock TacoRepository的依赖项。
我尝试过使用TacoRepository而不是TacoService来创建测试模块,但这会导致错误,抱怨“Nest无法解决RootTestModule上下文中的依赖关系”。任何帮助或建议都将非常感谢。

huwehgph

huwehgph1#

在其核心,Test.createTestingModule使用 * 标准 * 依赖注入功能创建了一个 * 标准 * NestJS模块。(Test.createTestingModule({ providers: [TacoService] })),测试模块必须能够解析该提供程序的 * 所有依赖项(在您的示例中,这将是TacoService的所有依赖项-即TacoRepository,UserRepository,BurritoRepository和MakerService),以及依赖关系的依赖关系等等-整个依赖关系树。如果它不能做到这一点,它会抱怨(因为它确实-“不能解决依赖关系...”)。
您使用useMocker函数的方式确保了 * 每个具有函数类型标记 *(本质上是类)的提供程序都被模拟(我相信不包括TacoService本身)。
现在,TacoRepository也有依赖项(toppingRepo,sauceRepo等),但这些依赖项的注入令牌(我假设)是 * 字符串
而不是函数。如果你模拟整个TacoRepository(这是你的代码中的情况),你就不需要再模拟它的依赖项了,所以代码运行得很好。
但是,如果您将TacoRepository添加到providers数组中,它的依赖项不会被模拟(因为它们的注入令牌是字符串,而您的useMocker只模拟函数令牌),NestJS无法解析它们并在运行测试时抛出错误。
注射代币是什么?这需要你自己去研究。但是我会说,当你写@Inject(Topping.name)时,Topping.name充当了一个注入令牌,它可能是一个字符串,对吗?所以你需要为这个令牌定义一些提供者,这样NestJS就知道在那里注入什么。
这是解决问题的一种方法:

beforeAll(async () => {
    const module = await Test.createTestingModule({
        providers: [
            TacoRepository,
            {
                provide: Topping.name,
                useValue: {} // Your mock implementation
            },
            {
                provide: Sauce.name,
                useValue: {} // Your mock implementation
            }
            // ... any other dependencies of TacoRepository must be mocked
        ],
        exports: [TacoRepository]
    })
        .compile();
});

字符串
或者你可以用=== "string"条件来补充useMocker中的=== "function"条件:

useMocker(token => {
    if (typeof token === "function") {
        const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
        const Mock = moduleMocker.generateFromMetadata(mockMetadata);
        return new Mock();
    }

    if (typeof token === "string") {
        // mock string providers
    }
}).compile();


然而,如果你真的想单独测试TacoRepository(模拟它的所有依赖项),你最好不要完全使用Test模块。这是你在NestJS的文档中找到的第一个测试示例。

describe("TacoRepository test suite", () => {
    const toppingRepositoryMock = {}; // Create whatever mock you think is appropriate
    const sauceRepositoryMock = {}; // ... same
    const tacoRepository = new TacoRepository(toppingRepositoryMock, sauceRepositoryMock);

    describe("Get taco string tests", () => {
        let actual;

        it("should return an empty taco string when the limit and/or offset are undefined", () => {
            actual = tacoRepository.getTacoString(undefined, undefined);
            expect(actual).toEqual("");
            actual = tacoRepository.getTacoString(undefined, 10);
            expect(actual).toEqual("");
            actual = tacoRepository.getTacoString(0, undefined);
            expect(actual).toEqual("");
        });
    });
});


如果您希望模拟除了TacoRepository之外的所有内容,您可以尝试修改useMocker的使用以忽略该特定令牌。

beforeAll(async () => {
    const module = await Test.createTestingModule({
        providers: [TacoService],
        exports: [TacoService]
    })
        .useMocker(token => {
            // Note the additional condition
            if (typeof token === "function" && token !== TacoRepository) {
                // ... create your mock
            }
        })
        .compile();
});

相关问题