
Product
Socket for Jira Is Now Available
Socket for Jira lets teams turn alerts into Jira tickets with manual creation, automated ticketing rules, and two-way sync.
jest-auto-spies
Advanced tools
Create automatic spies from classes in Jest tests, also for promises and observables
Easy and type safe way to write spies for jest tests, for both sync and async (promises, Observables) returning methods.
pnpm add -D jest-auto-spies
or
npm install -D jest-auto-spies
You've probably seen this type of manual spies in tests:
let mySpy = {
myMethod: jest.fn(),
};
The problem with that is first -
If you need to create a spy from any class, just do:
const myServiceSpy = createSpyFromClass(MyService);
THAT'S IT!
If you're using TypeScript, you get EVEN MORE BENEFITS:
const myServiceSpy: Spy<MyService> = createSpyFromClass(MyService);
Now that you have an auto spy you'll be able to:
✅ Have a spy with all of its methods generated automatically as "spy methods".
✅ Rename/refactor your methods and have them change in ALL tests at once
✅ Asynchronous helpers for Promises and Observables.
✅ Conditional return values with calledWith and mustBeCalledWith
✅ Have Type completion for both the original Class and the spy methods
✅ Spy on getters and setters
✅ Spy on Observable properties
my-component.jsexport class MyComponent {
constructor(myService) {
this.myService = myService;
}
init() {
this.compData = this.myService.getData();
}
}
my-service.jsexport class MyService{
getData{
return [
{ ...someRealData... }
]
}
}
my-spec.jsimport { createSpyFromClass } from 'jest-auto-spies';
import { MyService } from './my-service';
import { MyComponent } from './my-component';
describe('MyComponent', () => {
let myServiceSpy;
let componentUnderTest;
beforeEach(() => {
// 👇
myServiceSpy = createSpyFromClass(MyService); // <- THIS IS THE IMPORTANT LINE
componentUnderTest = new MyComponent(myServiceSpy);
});
it('should fetch data on init', () => {
const fakeData = [{ fake: 'data' }];
myServiceSpy.getData.mockReturnValue(fakeData);
componentUnderTest.init();
expect(myServiceSpy.getData).toHaveBeenCalled();
expect(componentUnderTest.compData).toEqual(fakeData);
});
});
TestBed.inject<any>(...)⚠ Make sure you cast your spy with any when you inject it:
import { MyService } from './my-service';
import { Spy, createSpyFromClass } from 'jest-auto-spies';
let serviceUnderTest: MyService;
// 👇
let apiServiceSpy: Spy<ApiService>;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
MyService,
// 👇
{ provide: ApiService, useValue: createSpyFromClass(ApiService) },
],
});
serviceUnderTest = TestBed.inject(MyService);
// 👇
apiServiceSpy = TestBed.inject<any>(ApiService);
});
// my-service.ts
class MyService{
getName(): string{
return 'Bonnie';
}
}
// my-spec.ts
import { Spy, createSpyFromClass } from 'jest-auto-spies';
import { MyService } from './my-service';
// 👇
let myServiceSpy: Spy<MyService>; // <- THIS IS THE IMPORTANT LINE
beforeEach( ()=> {
// 👇
myServiceSpy = createSpyFromClass( MyService );
});
it('should do something', ()=> {
myServiceSpy.getName.mockReturnValue('Fake Name');
... (the rest of the test) ...
});
For cases that you have methods which are not part of the Class prototype (but instead being defined in the constructor), for example:
class MyClass {
constructor() {
this.customMethod1 = function () {
// This definition is not part of MyClass' prototype
};
}
}
You can FORCE the creation of this methods spies like this:
// 👇
let spy = createSpyFromClass(MyClass, ['customMethod1', 'customMethod2']);
OR THIS WAY -
let spy = createSpyFromClass(MyClass, {
// 👇
methodsToSpyOn: ['customMethod1', 'customMethod2'],
});
Use the resolveWith or rejectWith methods.
⚠ You must define a return type : Promise<SomeType> for it to work!
// SERVICE:
class MyService {
// (you must define a return type)
// 👇
getItems(): Promise<Item[]> {
return http.get('/items');
}
}
// TEST:
import { Spy, createSpyFromClass } from 'jest-auto-spies';
let myServiceSpy: Spy<MyService>;
beforeEach(() => {
myServiceSpy = createSpyFromClass(MyService);
});
it(() => {
// 👇
myServiceSpy.getItems.resolveWith(fakeItemsList);
// OR
// 👇
myServiceSpy.getItems.rejectWith(fakeError);
// OR
// 👇
myServiceSpy.getItems.resolveWithPerCall([
// 👇 return this promise for the FIRST getItems() call
{ value: fakeItemsList },
// 👇 return this promise with a delay of 2 seconds (2000ms) for the SECOND getItems() call
{ value: someOtherItemsList, delay: 2000 },
]);
});
although with jest you don't really have to do that as you have the native mockResolvedValue and mockRejectedValue -
import { Spy, createSpyFromClass } from 'jest-auto-spies';
let myServiceSpy: Spy<MyService>;
beforeEach(() => {
myServiceSpy = createSpyFromClass(MyService);
});
it(() => {
// 👇
myServiceSpy.getItems.mockResolvedValue(fakeItemsList);
// OR
// 👇
myServiceSpy.getItems.mockRejectedValue(fakeError);
});
So the resolveWith and rejectWith are useful for backward compatibility if you're migrating from jasmine-auto-spies.
Use the nextWith or throwWith and other helper methods.
⚠ You must define a return type : Observable<SomeType> for it to work!
// SERVICE:
class MyService {
// (you must define a return type)
// 👇
getItems(): Observable<Item[]> {
return http.get('/items');
}
}
// TEST:
import { Spy, createSpyFromClass } from 'jest-auto-spies';
let myServiceSpy: Spy<MyService>;
beforeEach(() => {
myServiceSpy = createSpyFromClass(MyService);
});
it(() => {
// 👇
myServiceSpy.getItems.nextWith(fakeItemsList);
// OR
// 👇
myServiceSpy.getItems.nextOneTimeWith(fakeItemsList); // emits one value and completes
// OR
// 👇
myServiceSpy.getItems.nextWithValues([
{ value: fakeItemsList },
{ value: fakeItemsList, delay: 1000 },
{ errorValue: someError }, // <- will throw this error, you can also add a "delay"
{ complete: true }, // <- you can add a "delay" as well
]);
// OR
// 👇
const subjects = myServiceSpy.getItems.nextWithPerCall([
// 👇 return this observable for the FIRST getItems() call
{ value: fakeItemsList },
// 👇 return this observable after 2 seconds for the SECOND getItems call()
{ value: someOtherItemsList, delay: 2000 },
// 👇 by default, the observable completes after 1 value
// set "doNotComplete" if you want to keep manually emit values
{ value: someOtherItemsList, doNotComplete: true },
]);
subjects[2].next('yet another emit');
subjects[2].complete();
// OR
// 👇
myServiceSpy.getItems.throwWith(fakeError);
// OR
// 👇
myServiceSpy.getItems.complete();
// OR
// "returnSubject" is good for cases where you want
// to separate the Spy Observable creation from it's usage.
// 👇
const subject = myServiceSpy.getItems.returnSubject(); // create and get a ReplaySubject
subject.next(fakeItemsList);
});
If you have a property that extends the Observable type, you can create a spy for it as follows:
MyClass{
myObservable: Observable<any>;
mySubject: Subject<any>;
}
it('should spy on observable properties', ()=>{
let classSpy = createSpyFromClass(MyClass, {
// 👇
observablePropsToSpyOn: ['myObservable', 'mySubject']
}
);
// and then you could configure it with methods like `nextWith`:
// 👇
classSpy.myObservable.nextWith('FAKE VALUE');
let actualValue;
classSpy.myObservable.subscribe((value) => actualValue = value )
expect(actualValue).toBe('FAKE VALUE');
})
calledWith() - conditional return valuesYou can setup the expected arguments ahead of time
by using calledWith like so:
// 👇
myServiceSpy.getProducts.calledWith(1).returnValue(true);
and it will only return this value if your subject was called with getProducts(1).
// 👇 👇
myServiceSpy.getProductsPromise.calledWith(1).resolveWith(true);
// OR
myServiceSpy.getProducts$.calledWith(1).nextWith(true);
// OR ANY OTHER ASYNC CONFIGURATION METHOD...
mustBeCalledWith() - conditional return values that throw errors (Mocks)// 👇
myServiceSpy.getProducts.mustBeCalledWith(1).returnValue(true);
is the same as:
myServiceSpy.getProducts.mockReturnValue(true);
expect(myServiceSpy.getProducts).toHaveBeenCalledWith(1);
But the difference is that the error is being thrown during getProducts() call and not in the expect(...) call.
If you have a property that extends the Observable type, you can create a spy for it.
You need to configure whether you'd like to create a "SetterSpy" or a "GetterSpy" by using the configuration settersToSpyOn and GettersToSpyOn.
This will create an object on the Spy called accessorSpies and through that you'll gain access to either the "setter spies" or the "getter spies":
// CLASS:
MyClass{
private _myProp: number;
get myProp(){
return _myProp;
}
set myProp(value: number){
_myProp = value;
}
}
// TEST:
let classSpy: Spy<MyClass>;
beforeEach(()=>{
classSpy = createSpyFromClass(MyClass, {
// 👇
gettersToSpyOn: ['myProp'],
// 👇
settersToSpyOn: ['myProp']
});
})
it('should return the fake value', () => {
// 👇 👇 👇
classSpy.accessorSpies.getters.myProp.mockReturnValue(10);
expect(classSpy.myProp).toBe(10);
});
it('allow spying on setter', () => {
classSpy.myProp = 2;
// 👇 👇 👇
expect(classSpy.accessorSpies.setters.myProp).toHaveBeenCalledWith(2);
});
You can create an "auto spy" for a function using:
import { createFunctionSpy } from 'jest-auto-spies';
describe('Testing a function', () => {
it('should be able to spy on a function', () => {
function addTwoNumbers(a, b) {
return a + b;
}
// 👇 👇
const functionSpy = createFunctionSpy<typeof addTwoNumbers>('addTwoNumbers');
functionSpy.mockReturnValue(4);
expect(functionSpy()).toBe(4);
});
});
Could also be useful for Observables -
// FUNCTION:
function getResultsObservable(): Observable<number> {
return of(1, 2, 3);
}
// TEST:
it('should ...', () => {
const functionSpy =
createFunctionSpy<typeof getResultsObservable>('getResultsObservable');
functionSpy.nextWith(4);
// ... rest of the test
});
Here's a nice trick you could apply in order to spy on abstract classes -
// 👇
abstract class MyAbstractClass {
getName(): string {
return 'Bonnie';
}
}
describe(() => {
// 👇
abstractClassSpy = createSpyFromClass<MyAbstractClass>(MyAbstractClass as any);
abstractClassSpy.getName.mockReturnValue('Evil Baboon');
});
And if you have abstract methods on that abstract class -
abstract class MyAbstractClass {
// 👇
abstract getAnimalName(): string;
}
describe(() => {
// 👇
abstractClassSpy = createSpyFromClass<MyAbstractClass>(MyAbstractClass as any, [
'getAnimalName',
]);
// OR
abstractClassSpy.getAnimalName.mockReturnValue('Evil Badger');
});
createObservableWithValues() - Create a pre-configured standalone observableMOTIVATION: You can use this in order to create fake observable inputs with delayed values (instead of using marbles).
Accepts the same configuration as nextWithValues but returns a standalone observable.
EXAMPLE:
//
import { createObservableWithValues } from 'jasmine-auto-spies';
it('should emit the correct values', () => {
// 👇
const observableUnderTest = createObservableWithValues([
{ value: fakeItemsList },
{ value: secondFakeItemsList, delay: 1000 },
{ errorValue: someError }, // <- will throw this error, you can also add a "delay" to the error
{ complete: true }, // <- you can also add a "delay" to the complete
]);
});
And if you need to emit more values, you can set returnSubject to true and get the subject as well.
it('should emit the correct values', () => {
// 👇 👇
const { subject, values$ } = createObservableWithValues(
[
{ value: fakeItemsList },
{ value: secondFakeItemsList, delay: 1000 },
{ errorValue: someError }, // <- will throw this error, you can also add a "delay" to the error
{ complete: true }, // <- you can also add a "delay" to the complete
],
// 👇
{ returnSubject: true }
);
subject.next(moreValues);
});
provideAutoSpy() - Small Utility for Angular TestsThis will save you the need to type:
{ provide: MyService, useValue: createSpyFromClass(MyService, config?) }
INTERFACE: provideAutoSpy(Class, config?)
USAGE EXAMPLE:
TestBed.configureTestingModule({
providers: [
MyComponent,
provideAutoSpy(MyService)
];
})
myServiceSpy = TestBed.inject<any>(MyService);
Want to contribute? Yayy! 🎉
Please read and follow our Contributing Guidelines to learn what are the right steps to take before contributing your time, effort and code.
Thanks 🙏
Be kind to each other and please read our code of conduct.
Thanks goes to these wonderful people (emoji key):
Shai Reznik 💻 📖 🤔 🚇 🚧 🧑🏫 👀 ⚠️ | Guille Eneas Timón Grau 💻 ⚠️ | Rainer Hahnekamp 🚧 |
This project follows the all-contributors specification. Contributions of any kind welcome!
MIT
FAQs
Create automatic spies from classes in Jest tests, also for promises and observables
The npm package jest-auto-spies receives a total of 12,383 weekly downloads. As such, jest-auto-spies popularity was classified as popular.
We found that jest-auto-spies demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Socket for Jira lets teams turn alerts into Jira tickets with manual creation, automated ticketing rules, and two-way sync.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.