Node.js 如何仅对基类方法而不是重写的方法使用jest.spyOn

Node.js 如何仅对基类方法而不是重写的方法使用jest.spyOn,node.js,jestjs,nestjs,Node.js,Jestjs,Nestjs,正在尝试为我的nestjs应用程序编写测试脚本 我有一个控制器/服务框架,如下所示: 控制器: import { Controller, Get } from '@nestjs/common'; ... @Controller() export class MyController { ... } 现在,显然findAll的mock实现在MyService上模拟findAll,因此测试失败,因为从未调用spy_缓存集 我想做的是只模拟DbService中的基本方法findAll,以便维

正在尝试为我的nestjs应用程序编写测试脚本

我有一个控制器/服务框架,如下所示:

控制器:

import { Controller, Get } from '@nestjs/common';

...

@Controller()
export class MyController {

...

}
现在,显然findAll的mock实现在MyService上模拟findAll,因此测试失败,因为从未调用spy_缓存集

我想做的是只模拟DbService中的基本方法findAll,以便维护MyService中存在的额外功能

有没有一种方法可以做到这一点,而不只是重命名MyService中的方法,我宁愿避免这样做

编辑以添加: 感谢@Jonatan lenco提供如此全面的回复,我已经采纳并实施了这一回复。 我还有一个问题。CacheService、DbService和许多其他东西,其中一些我想模仿,另一些我不想模仿的东西在一个外部库项目中,共享

cache.service.ts

导出类缓存服务{…}

索引

然后将其编译并作为包包含在node_模块中

在我编写测试的项目中:

import { CacheService, DocumentService, OtherStuff } from "shared";

我是否仍可以仅对CacheService使用jest.mock,而不模拟整个共享项目?

在这种情况下,由于您希望监视抽象类DbService,因此可以监视prototype方法:

jest.spyOn(DbService.prototype, 'findAll').mockImplementation(async () => {
  return [testPerson];
});
这里还有一些关于使用NestJS和Jest进行单元测试的建议:

在本例中,使用jest.mock简化对CacheService的模拟。看

当您执行jest.spyOn时,您可以断言方法的执行,而不需要spy对象。而不是:

你可以做:

jest.spyOn(DbService.prototype, 'findAll').mockImplementation(async () => {
  return [testPerson];
});

...

expect(DbService.prototype.findAll).toBeCalledTimes(1);
如果您正确地模拟了一个类,那么如果您不想模拟它的实现,就不需要监视该方法

使用NestJS中的测试实用程序,当您有复杂的依赖项注入时,它将特别帮助您。看

下面是一个将这4条建议应用于单元测试的示例:

import { Test } from '@nestjs/testing';

import { CacheService } from './cache.service';
import { DbService } from './db.service';
import { MyController } from './my.controller';
import { MyService } from './my.service';
import { Person } from './person';

jest.mock('./cache.service');

describe('MyController', async () => {
  let myController: MyController;
  let myService: MyService;
  let cacheService: CacheService;
  const testPerson = new Person();

  beforeAll(async () => {
    const module = await Test.createTestingModule({
      controllers: [MyController],
      providers: [
        MyService,
        CacheService,
      ],
    }).compile();

    myService = module.get<MyService>(MyService);
    cacheService = module.get<CacheService>(CacheService);
    myController = module.get<MyController>(MyController);

    jest.spyOn(DbService.prototype, 'findAll').mockImplementation(async () => {
      return [testPerson];
    });
  });

  beforeEach(async () => {
    jest.clearAllMocks();
  });

  describe('getAll', () => {
    it('Should return an array of one person', async () => {
      const r = await myController.getAll();
      expect(r).toHaveLength(1);
      expect(DbService.prototype.findAll).toBeCalledTimes(1);
      expect(cacheService.setValue).toBeCalledTimes(1);
      expect(r).toEqual([testPerson]);
    });
  });
});
关于模拟另一个包的特定项,而不是模拟整个包,您可以执行以下操作:

在等级库文件中创建一个类,也可以在导入的另一个文件中创建,甚至在共享模块中创建,该模块具有不同的名称,但具有相同的公共方法名称。请注意,我们使用jest.fn,因为我们不需要提供实现,并且已经在该方法中进行了间谍操作的人员以后不需要执行jest.spyOn,除非您必须模拟该实现。 设置测试模块的提供程序时,告诉它您提供的是原始类,但实际上提供的是模拟类:
有关提供商的更多信息,请参见Nest遵循相同的Angular理念。

非常感谢您的详细回复。如果您有几分钟的时间,我将非常感谢您快速查看我编辑中的问题。再次感谢!谢谢你的好意。我已经编辑了回复,为您的最后一个问题添加了答案。
import { CacheService, DocumentService, OtherStuff } from "shared";
jest.spyOn(DbService.prototype, 'findAll').mockImplementation(async () => {
  return [testPerson];
});
spy_findall = jest.spyOn(myService, "findAll").mockImplementation(async () => {
  return [testPerson];
});

...

expect(spy_findall).toBeCalledTimes(1);
jest.spyOn(DbService.prototype, 'findAll').mockImplementation(async () => {
  return [testPerson];
});

...

expect(DbService.prototype.findAll).toBeCalledTimes(1);
import { Test } from '@nestjs/testing';

import { CacheService } from './cache.service';
import { DbService } from './db.service';
import { MyController } from './my.controller';
import { MyService } from './my.service';
import { Person } from './person';

jest.mock('./cache.service');

describe('MyController', async () => {
  let myController: MyController;
  let myService: MyService;
  let cacheService: CacheService;
  const testPerson = new Person();

  beforeAll(async () => {
    const module = await Test.createTestingModule({
      controllers: [MyController],
      providers: [
        MyService,
        CacheService,
      ],
    }).compile();

    myService = module.get<MyService>(MyService);
    cacheService = module.get<CacheService>(CacheService);
    myController = module.get<MyController>(MyController);

    jest.spyOn(DbService.prototype, 'findAll').mockImplementation(async () => {
      return [testPerson];
    });
  });

  beforeEach(async () => {
    jest.clearAllMocks();
  });

  describe('getAll', () => {
    it('Should return an array of one person', async () => {
      const r = await myController.getAll();
      expect(r).toHaveLength(1);
      expect(DbService.prototype.findAll).toBeCalledTimes(1);
      expect(cacheService.setValue).toBeCalledTimes(1);
      expect(r).toEqual([testPerson]);
    });
  });
});
import { Controller, Get } from '@nestjs/common';

...

@Controller()
export class MyController {

...

}
class CacheServiceMock {
  setValue = jest.fn();
}
const module = await Test.createTestingModule({
  controllers: [MyController],
  providers: [
    MyService,
    { provide: CacheService, useClass: CacheServiceMock },
  ],
}).compile();