Jasmine .toHaveBeenCalledWith 何时匹配参数?

9
我有以下的Angular服务和它的Jasmine测试。这个测试调用f1()并对f2()进行间谍监视。函数f2接受变量v2并修改它(将字段a设置为3)。函数f2应该使用v2(如在f1中声明),但我的测试在toHaveBeenCalledWith上失败,并且说实际调用是在f2函数调用后的对象。Jasmine是否匹配toHaveBeenCalledWith的参数,这不应该是推荐的方式,还是我在这里犯了一些错误。
服务:
export class JasmineTestClass{
    constructor(){
    }
    f2(v2){
        v2.a = 3
    };
    f1(v1){
        let v2 = {
            a:30
        };
        this.f2(v2);
    }
}

测试:

describe('Test', () => {
    let service: JasmineTestClass;
    beforeEach(() => {
        service = new JasmineTestClass();
        spyOn(service, 'f2').and.callThrough();
    });
    let v1 = {
        a:2, b:3
    };
    let v2 = {
        a:30
    };
    it('should succeed', () => {
        service.f1(v1);
        expect(service.f2).toHaveBeenCalledWith(v2);    //this is failing
    });
})

日志:

Test should succeed FAILED

Expected spy f2 to have been called with [Object ({a:30})] but actual calls were [Object ({a:3})]

请注意,在测试过程中,我使用Chrome进行了调试,并且函数f2()是使用v2 = {a:30}调用的。
1个回答

11

toHaveBeenCalledWith会在被断言时匹配调用参数。

Jasmine Spies会在内部保存参数引用。可以通过记录service.f2.calls.all()对象来跟踪调用参数。

这里的问题是f2修改了传递给它的对象的引用。原始的v2.a === 30f2调用后就不存在了。

针对这种情况的正确策略是创建细粒度的测试,每个单元(方法)一个测试。使用callThrough表明各个单元不是相互隔离的,因此默认情况下最好只使用spyOn(service, 'f2')桩:

it('should succeed', () => {
  service.f1(v1);
  expect(service.f2).toHaveBeenCalledWith({ a:30 });
});

it('should succeed', () => {
  const obj = { a:30 };
  service.f2(obj);
  expect(obj).toEqual({ a:3 });
});

现在我们正在测试这两种方法究竟发生了什么。


1
我也曾经掉进同样的陷阱。为了在我的间谍对象上使用不同的 expect,我只需在 jasmine 测试的顶层声明它们,并在 beforeEach 块中每次初始化它们。这使得在“相同”的间谍对象的不同测试中期望不同的情况成为可能。 - mentalo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接