【问题标题】:NestJS accessing private class field before testing method with jestNestJS 在用玩笑测试方法之前访问私有类字段
【发布时间】:2020-11-17 21:07:37
【问题描述】:

假设有以下嵌套服务类,其中 private 字段 myCache 和公共方法 myFunction

import * as NodeCache from 'node-cache'
class MyService{
    private myCache = new NodeCache();

    myFunction() {
        let data = this.myCache.get('data');
        if(data === undefined){
            // get data with an http request and store it in this.myCache with the key 'data'
        } 
        return data;
    }
}

我想针对两种不同的情况测试函数 myFunction。 第一种情况:如果条件为真。第二种情况:如果条件为假。

这是缺少两个测试的测试类:

import { Test, TestingModule } from '@nestjs/testing';
import { MyService} from './myService';

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

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

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

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  describe('myFunction', () => {
      it('should return chached data', () => {
          // first test
      }),
      it('should return new mocked data', () => {
          // second test
      })
   })
});

因此我想我必须访问或模拟 myCache 私有类字段。 因为它是私有的,所以我无法在测试类中访问它。

我的问题是:实现这一目标的最佳和正确方法是什么?

【问题讨论】:

  • 私人会员可以通过service['myCache']访问。可能不需要这样做,而是mock需要mock的数据源,也就是NodeCache。
  • 好的,现在我知道如何访问它了,但是我如何才能以正确的方式用 jest 来模拟它呢?

标签: javascript unit-testing mocking jestjs nestjs


【解决方案1】:

如果你只是想模拟它,你总是可以使用 as any 告诉 Typescript 不要警告你访问私有值。

jest.spyOn((service as any).myCache, 'get').mockReturnValueOnce(someValue);

但是,必须一遍又一遍地做这有点烦人,这并不是最佳实践。相反,我要做的是将您的缓存移动为可注入的提供程序,以便可以立即将其换出,并且您的MyService 不再对node-cache 有硬依赖。像这样的:

// my.module.ts
@Module({
  providers: [
    MyService,
    {
      provide: 'CACHE',
      useClass: NodeCache
    }
  ]
})
export class MyModule {}

// my.service.ts
@Injectable()
export class MyService {
  constructor(@Inject('CACHE') private readonly myCache: NodeCache) {}
...

现在在您的测试中,您可以将 CACHE 令牌换成一个模拟实现,该实现也可以在您的 beforeEach 块中检索,这意味着不再有任何。

describe('MyService', () => {
  let service: MyService;
  let cache: { get; set; }; // you can change the type here
  
  beforeEach(async () => {
    const modRef = await Test.createTestingModule({
      providers: [
        MyService,
        {
          provide: 'CACHE',
          useValue: { get: jest.fn(), set: jest.fn() }
        }
      ]
    }).compile();
    service = modRef.get(MyService);
    cache = modRef.get<{ get; set; }>('CACHE');
  });
});

现在您可以在不使用as any 的情况下拨打jest.spyOn(cache, 'get')

【讨论】:

  • 它可以由 DI 处理,但这可以用 Jest 单独处理,间谍没有什么烦人的。对于每个测试的模拟,可以在beforeEach 中模拟一次。对于多个测试中的模拟,有 __mocks__ 用于可重复使用的模拟。
  • 可能是这样,但是如果您每次在测试中都需要更改模拟,则必须继续使用我一开始的jest.spyOn((service as any).myCache, 'get'),这会失去类型安全性并需要有更多时间写作
  • 如果NodeCache 已经被模拟了,你可以在方法上执行mockImplementation 并且可能在每个测试中重新导入模拟模块以获得新的模拟。这就是 Jest 开箱即用的功能,它并不比 Nest 的 DI 开销大。 DI 是 Jasmine 和 Angular 的杀手级功能,但在 Jest 中很难销售,因为 Node 模块很容易被模拟。
猜你喜欢
  • 2019-09-26
  • 2021-04-09
  • 2020-08-03
  • 2019-12-19
  • 2015-03-07
  • 2018-11-23
  • 1970-01-01
  • 2021-12-31
  • 2020-03-25
相关资源
最近更新 更多