How To Spy on Classes
Tags: javascript, testing, tutorial, webdev
Published: 2023-06-28 07:16:19

[Spying](https://jestjs.io/docs/jest-object#jestspyonobject-methodname) in Jest allows us to mock classes while keeping the original implementation intact. A `spy` can help us verify if a certain method was invoked, without interfering with its functionality. However, the process of spying can vary, especially when dealing with class methods versus simple functions. There are three main scenarios we need to deal with: spying on static methods, spying on methods of a single instance, and spying on methods of all instances of a class.

## Spying on Static Methods
Static methods are associated with the class itself, not with any specific instance of the class. This makes spying on them relatively straightforward. We simply reference the class and specify which static method we want to spy on.

```js
class Person {
  static hello() {
    return 'Hello, I am a static method.';
  }

  goodbye() {
    return 'Goodbye, I am an instance method.';
  }
}
test('should call static method', () => {
  // Spy on the static method
  const spy = jest.spyOn(Person, 'hello');

  // Invoke the static method
  Person.hello();

  // Test if the static method has been called
  expect(spy).toHaveBeenCalled();
});
```

## Spying on Methods of a Single Instance
In scenarios where we have access to a particular instance of the class, we can set a spy on any of this instance's methods. We simply reference the instance and the method we want to spy on.

```js
class Person {
  static hello() {
    return 'Hello, I am a static method.';
  }

  goodbye() {
    return 'Goodbye, I am an instance method.';
  }
}

test('should call instance method of single object', () => {
  // Create an instance of the class
  const person = new Person();

  // Spy on the instance method
  const spy = jest.spyOn(person, 'goodbye');

  // Invoke the method on the instance
  person.goodbye();

  // Test if the method on the instance has been called
  expect(spy).toHaveBeenCalled();
});
```

## Spying on Methods of All Instances
In many real-world cases, we may not have access to the specific instance of the class we want to spy on. This instance might be hidden in a module we are testing and not directly exported or accessible from our test case. In such situations, we may choose to spy on all instances of a class. To do this, we reference the prototype of the class and the method we want to spy.

```js
class Person {
  static hello() {
    return 'Hello, I am a static method.';
  }

  goodbye() {
    return 'Goodbye, I am an instance method.';
  }
}

// Create an instance and call the instance method
function goodbye() {
  const person = new Person();
  return person.goodbye();
}

test('should call instance method of all objects', () => {
  // Spy on the instance method for all instances
  const spy = jest.spyOn(Person.prototype, 'goodbye');

  // Call function without access to underlying instance
  goodbye();
  goodbye();

  // Test if the method has been called on any instance
  expect(spy).toHaveBeenCalledTimes(2);
});
```

---

I hope you found this post helpful. If you have any questions or comments, feel free to leave them below. If you'd like to connect with me, you can find me on [LinkedIn](https://www.linkedin.com/in/christian-zirkel/) or [GitHub](https://github.com/zirkelc). Thanks for reading!