jasmine.createSpyObj with properties

Per the documentation (emphasis mine):

You can create a spy object with several properties on it quickly by
passing an array or hash of properties as a third argument to
createSpyObj. In this case you won’t have a reference to the created
spies, so if you need to change their spy strategies later, you will
have to use the Object.getOwnPropertyDescriptor approach
.

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);
});

Spied properties are descriptors (see e.g. Object.defineProperty on MDN), so to access the spy objects you need to get the descriptor object then interact with the get and set methods defined on it.


In TypeScript, the compiler needs a bit of help. createSpyObj returns either any or SpyObj<T>, and a SpyObj only defines the methods as being spied on:

type SpyObj<T> = T & {
    [K in keyof T]: T[K] extends Func ? T[K] & Spy<T[K]> : 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 (as Object.getOwnPropertyDescriptor may return undefined) and a type assertion to a Spy:

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

Playground

Leave a Comment