首页 > 解决方案 > Angular - 对组件构建中的承诺进行单元测试

问题描述

假设以下组件的构造函数和函数调用:

constructor(private fooService: FooService){
    this.getBarFromService()
}

getBarFromService = () => fooService.getBar().then(bar => this.bar = bar)

服务的粗制getBar()函数应该等待 API 结果:

getBar() {
    let bar = null;
    await new Promise((resolve, reject) => {
        this.barBuilder.get('/bar').then(
            data => resolve(bar = data),             // data = baz
            error => reject(error)
        )
    return bar;
}

我现在想尝试编写一个单元/集成测试来确定组件可以在不调用 API 的情况下请求和显示数据。从我读过的内容来看,我需要模拟该服务,以便它不会barBuilder在现实中调用,而是立即返回一个值。这导致了以下测试文件:

beforeEach(() =>{
    let mockFooService = jasmine.createSpyObj('FooService', ['getBar']);
    mockFooService.getBar.and.returnValue(Promise.resolve(baz))

    TestBed.configureTestingModule({
        providers: [{provide: FooService, useValue: mockFooService}],
    }).compileComponents();
});

describe('get bar should return baz', () => {
    expect(component).toBeTruthy();
    component.getBar();
    fixture.detectChanges();
    expect(component.bar).toBe(baz)
}

由于原始getBar()函数在已解析的 Promise 对象中返回数据,而不是数据本身,因此我必须重新创建所述解析,否则测试将失败,说它无法处理then()调用块。这样做后,它给了我一个想法,即测试在功能级别上工作,随着测试的进行,我会假设then()块中的操作已经执行。

但是,baz在构造和手动调用之后,该值尚未应用于组件,并且没有指针可以查看错误所在。在每个期望之间随意投掷console.log()表明component.bar在每个点上都保持为空。我也没有找到一个合理的答案,比如使用fakeAsync等等。不适用,因为getBarFromService()函数本身不是异步的。

因此,我不知所措,Angular (v8) 和 Jasmine 的文档都被严格遵守(尽我所能。)

问题响起;我如何能够找到为什么以及在模拟和/或单元测试本身baz中没有应用变量的位置component.bar,以及我将如何解决它?

标签: angularunit-testing

解决方案


你必须使用async()whenStable()

describe('get bar should return baz', async(() => {
  expect(component).toBeTruthy();
  component.getBar();
  fixture.detectChanges();

  fixture.whenStable().then(() => {
    expect(component.bar).toBe(baz);
  });
}));

附带说明一下,您getBar可以大大简化为:

getBar() {
  return this.barBuilder.get('/bar');
}

.get方法已经返回了一个承诺。因此无需将其包装在new Promise. 如果你想在你的方法中使用 bar 变量,你可以这样做:

async getBar() {
  const bar = await this.barBuilder.get('/bar');

  return bar;
}

推荐阅读