具有属性的 jasmine.createSpyObj

新手上路,请多包涵

在我的 Angular 测试中模拟依赖项时,我通常使用 jasmine.createSpyObj 创建一个间谍对象:

 const serviceSpy= jasmine.createSpyObj('MyService', ['method']);

然后将其提供给 TestBed:

   providers: [
    {provide: MyService, useValue: serviceSpy}
  ]

当我在测试中使用它时,我可以指定所需的返回值:

 serviceSpy.method.and.returnValue(of([...]));

现在我还需要模拟属性,但我不知道应该如何完成。 createSpyObj 允许定义属性名称:

 const serviceSpy= jasmine.createSpyObj('MyService', ['method'], ['property']);

但我已经根据大量文章和答案尝试了多种解决方案,但均未成功,例如:

 // Cannot read property 'and' of undefined
serviceSpy.property.and.returnValue(true);
// not declared configurable
spyOnProperty(serviceSpy, 'property').and.returnValue(true);
// no build errors, but value stays 'undefined'
serviceSpy.property = true;

我可以让它“一半”工作的唯一方法是:

 let fakeValue = true;
const serviceSpy= jasmine.createSpyObj('MyService', ['method'], {'property': fakeValue});

这里的问题是它在创建时是一次性的。如果我想改变测试中的期望值,它是行不通的。

 fakeValue = false;
serviceSpy.property ==> stays to the initial value 'true';

是否存在通过创建间谍对象来解决模拟方法和属性的解决方案,或者我是否应该创建自己的假类,然后我可以在其上使用 spyOnspyOnProperty

我还想知道 createSpyObj 定义中属性数组的用法。到目前为止,我还没有在网上看到任何解释它的例子。

原文由 Ronald 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 906
2 个回答

根据 文档(强调我的):

您可以通过将属性的数组或散列作为第三个参数传递给 createSpyObj 来快速创建一个具有多个属性的间谍对象。在这种情况下,您将没有对创建的间谍的引用,因此 如果您以后需要更改他们的间谍策略,则必须使用 Object.getOwnPropertyDescriptor 方法

>  it("creates a spy object with properties", function() {
>   let obj = createSpyObj("myObject", {}, { x: 3, y: 4 });
>   expect(obj.x).toEqual(3);
>
>   Object.getOwnPropertyDescriptor(obj, "x").get.and.returnValue(7);
>   expect(obj.x).toEqual(7);
> });
>
> ```

间谍属性是 _描述符_(参见 [MDN 上的例如 `Object.defineProperty`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) ),因此要访问间谍对象,您需要获取描述符对象然后与 `get` 和 `set` 552 交互上面定义的方法。

* * *

在 TypeScript 中,编译器需要一些帮助。 [`createSpyObj`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d063d0175b3eb59a7a4cc5e64992f8a220d9a910/types/jasmine/index.d.ts#L292-L295) returns either `any` or `SpyObj<T>` , and a `SpyObj` only defines the _methods_ as being spied on:

type SpyObj = T & { [K in keyof T]: T[K] extends Func ? T[K] & Spy : T[K]; // | if it’s a | spy on it | otherwise leave // | callable | | it alone };


So to access `.and` on the descriptor's getter, you'll need [optional chaining](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining) (as `Object.getOwnPropertyDescriptor` may return `undefined` ) and a [type assertion](https://www.typescriptlang.org/docs/handbook/basic-types.html#type-assertions) to a [`Spy`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d063d0175b3eb59a7a4cc5e64992f8a220d9a910/types/jasmine/index.d.ts#L959-L965) :

(Object.getOwnPropertyDescriptor(obj, “x”)?.get as Spy<() => number>).and.returnValue(7);

”`

操场

原文由 jonrsharpe 发布,翻译遵循 CC BY-SA 4.0 许可协议

非常感谢@jonrsharpe!我刚刚添加了一个功能,所以我可以这样做:

 spyPropertyGetter(spy, 'propName').and.returnValue(...)

定义为:

 function spyPropertyGetter<T, K extends keyof T>(
  spyObj: jasmine.SpyObj<T>,
  propName: K
): jasmine.Spy<() => T[K]> {
  return Object.getOwnPropertyDescriptor(spyObj, propName)?.get as jasmine.Spy<() => T[K]>;
}

原文由 Ian Grainger 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题