I have a class instance that should be cleared between each test, so I'm doing this:
class Foo {
constructor() {}
public bar() {
return console.log("bar")
}
}
describe('tests on class Foo', () => {
let foo: Foo
beforeEach(() => {
foo = new Foo()
jest.clearAllMocks()
})
const spyOnBar = jest.spyOn(foo, "bar")
test("should call bar", () => {
foo.bar()
expect(spyOnBar).toHaveBeenCalled()
})
})
but spyOnBar
keeps giving me the error Cannot spyOn on a primitive value; undefined given
.
there's a way to spy on method bar without declaring the spy inside each test?
CodePudding user response:
See Order of execution of describe and test blocks.
Let's see the order of the execution.
describe('tests on class Foo', () => {
beforeEach(() => {
console.log('beforeEach');
});
console.log('describe');
test('should call bar', () => {
console.log('test');
});
});
The logs:
PASS stackoverflow/72035574/foo.test.ts (10.376 s)
tests on class Foo
✓ should call bar (2 ms)
console.log
describe
at Suite.<anonymous> (stackoverflow/72035574/foo.test.ts:16:11)
console.log
beforeEach
at Object.<anonymous> (stackoverflow/72035574/foo.test.ts:13:13)
console.log
test
at Object.<anonymous> (stackoverflow/72035574/foo.test.ts:20:13)
When you call jest.spyOn(foo, 'bar')
method in describe
block, the foo
instance is not created yet. That's why you got the error.
Option 1. Create the foo
instance in the describe
block and add spy on its method. Since all test cases share one foo
instance and spy, we need to call jest.clearAllMocks()
in beforeEach
hook to clear the mock.calls
and mock.instances
properties so that each test case will use a clear spy object. The toBeCalledTimes(1)
will work.
class Foo {
constructor() {}
public bar() {
return console.log('bar');
}
}
describe('tests on class Foo', () => {
const foo = new Foo();
const spyOnBar = jest.spyOn(foo, 'bar');
beforeEach(() => {
jest.clearAllMocks();
});
test('should call bar', () => {
foo.bar();
expect(spyOnBar).toBeCalledTimes(1);
});
test('should call bar - 2', () => {
foo.bar();
expect(spyOnBar).toBeCalledTimes(1);
});
});
Option 2. Create foo
and spy in beforeEach
hook so that each test case will use a new one. There is no need to use jest.clearAllMocks()
.
describe('tests on class Foo', () => {
let spyOnBar: jest.SpyInstance;
let foo: InstanceType<typeof Foo>;
beforeEach(() => {
foo = new Foo();
spyOnBar = jest.spyOn(foo, 'bar');
});
test('should call bar', () => {
foo.bar();
expect(spyOnBar).toBeCalledTimes(1);
});
test('should call bar - 2', () => {
foo.bar();
expect(spyOnBar).toBeCalledTimes(1);
});
});