首页 > 解决方案 > 如何通过该服务的测试套件将不同的依赖模拟注入到服务中?

问题描述

在测试 Angular 服务(具有自己的依赖项)时,我经常发现自己陷入了测试测试套件中的快乐路径和悲伤路径。

我的意思是我有这项服务:

export class MyService {
  constructor(private MyClient) {}

  public doIt() {
    return this.myClient.doTheThing().pipe(
      catchError(error => {
        // error handling logic
      }),
      // process the response
    )
  }
}

现在我想测试MyService#doIt何时MyClient#doTheThing成功运行并返回该请求的响应/结果的可观察值,以及何时MyClient#doTheThing失败和错误(因为我有一些catchError要测试的逻辑)。

测试快乐路径很容易:

// MyClientMock is a mock implementation of MyClient that returns a canned result 
// for instance so that I can test against these values when running the tests.

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

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        MyService,
        { provide: MyClient, useClass: MyClientMock }
      ],
    })
    service = TestBed.inject(MyService)
  })

  it('does the thing', (done) => {
    service.doIt().subscribe(result => {
      expect(result).toEqual('something')
      done()
    })
  })
})

但是我如何测试悲伤的路径,MyClient而不是引发错误,并确保其中的逻辑catchError符合我的预期?

我考虑过创建另一个MyClientMock并命名它,比如说,MyClientErrorMock它会抛出。但是我不能注入它,因为它已经在测试开始时设置好了,它会干扰其他测试用例。

在这种情况下使用什么好的模式来测试依赖错误或成功时会发生什么?

标签: angularunit-testingjestjs

解决方案


我强烈推荐Jest而不是 Karma。

当您需要重新创建模拟函数的复杂行为以使多个函数调用产生不同的结果时,请使用 mockImplementationOnce 方法:

const myMockFn = jest
  .fn()
  .mockImplementationOnce(cb => cb(null, true))
  .mockImplementationOnce(cb => cb(null, false));

myMockFn((err, val) => console.log(val));
// > true

myMockFn((err, val) => console.log(val));
// > false

这是另一个模拟示例,它根据条件参数生成结果:

import { when } from 'jest-when';

const fn = jest.fn();
when(fn).calledWith(1).mockReturnValue('hello world!');

const result = fn(1);
expect(result).toEqual('hello world!');

我想说,Jest 相对于 Karma 的最大优势在于其非隐蔽、直接的错误消息,让您一眼就能确定测试失败的根本原因。


推荐阅读