业力:如何处理对具有不同响应的间谍的多次调用?

时间:2017-02-16 18:27:15

标签: angular jasmine karma-jasmine

我有一个我想要测试的方法,在我为两个单独的元素调用document.getElementById的方法中。在过去,我刚刚在document.getElementById方法上创建了一个间谍,并将返回值设置为我选择的模拟元素。但是现在我需要第一次调用间谍返回一个模拟元素,然后下一个调用返回第二个独特元素,我不确定如何实现这一点。

代码背后:

private scrollParentToChild(): void {
  let parent: HTMLElement = document.getElementById('.theParent');
  let child: HTMLElement = document.getElementById('.theChild');
  parent.scrollTop = child.offsetTop;
}

单元测试:

describe('scrollParentToChild', () => {
  it('should set the parents scrollTop to the offsetTop of the child', () => {
    let parent: any = {
      scrollTop: 0
    };
    let child: any = {
      offsetTop: 100
    };
    spyOn(document, 'getElementById').and.returnValue(parent); // Okay this will return the parent for both calls to document.getElementById now, but how do I make it only return this for the first call and then return child for the second call.
    component.scrollParentToChild();
    expect(parent.scrollTop).toEqual(child.offsetTop);
  }
});

2 个答案:

答案 0 :(得分:1)

只要您使用的是较新版本的Jasmine(至少2.4),您就应该能够在Jasmine间谍上利用.and.returnValues(...)方法。您可以按以下方式使用它:

spyOn(document, 'getElementById').and.returnValues(parent, child}; //note the 's'!

这样,对getElementById的第一次调用将返回parent,第二次将返回child,随后的调用将返回undefined

Jasmine docs for returnValues

答案 1 :(得分:0)

茉莉花间谍真的很简单,缺乏特色。对于更复杂的场景,可以使用Sinon存根:

const mockedGetElementById = sinon.stub(document, 'getElementById');
mockedGetElementById.withArgs('.theParent').returns(parent);
mockedGetElementById.withArgs('.theChild').returns(child);
...
mockedGetElementById.restore();

可以使用Jasmine间谍手动处理多个调用。可以根据document.getElementById.calls.count()有条件地返回值,但在这种情况下,它是定义返回值的函数参数,而不是顺序。它会是这样的:

spyOn(document, 'getElementById').and.callFake((id) => {
  switch (id) {
    case '.theParent':
      return parent;
    case '.theChild':
      return child;
    default:
      throw new Error('Unexpected call');
  }  
});

应该注意的是,测试框架是Jasmine。在这种情况下,如果它与Karma一起运行并不重要。