Sinon spy callCount returns 0 - javascript

I can't seem to get my test working. I have a simple mixin like so:
export const mixin = superclass => class mixin extends superclass {
constructor() {
super();
this.addEventListener('do-it', this.doIt);
}
doIt() {
console.log('did it');
}
};
And a simple test as well:
describe('mixin', () => {
it('should call doIt', () => {
class TestElement extends mixin(HTMLElement) {}
customElements.define('test-element', TestElement);
const el = new TestElement();
const spy = sinon.spy(el, 'doIt');
el.dispatchEvent(new CustomEvent('do-it'));
expect(spy.callCount).to.equal(1);
});
});
https://jsfiddle.net/nbuLhvkd/
It logs did it but the spy's callCount value is 0. If I do const spy = sinon.spy(console, 'log');, the spy's callCount is 1. What's the correct way of spying for instance methods?

It is likely that your 'dispatchEvent' call is asynchronous, therefore the callCount is indeed 0 since it is executed synchronously.
Otherwise your syntax is good - as your test on console call prooved.

I used TestElement.prototype for spying and also moved it before instantiating new TestElement();. It now works but can someone explain why?
describe('Sinon examples', () => {
it('logs test on do-it', () => {
class TestElement extends mixin(HTMLElement) {}
customElements.define('test-element', TestElement);
const spy = sinon.spy(TestElement.prototype, 'doIt');
const el = new TestElement();
el.dispatchEvent(new CustomEvent('do-it'));
expect(spy.calledOnce).to.be.true;
});
});
https://jsfiddle.net/h0f9Le16/

Related

Jest - Mock a Class from a Module (and a function that uses the get keyword) [duplicate]

In Sinon I can do the following:
var myObj = {
prop: 'foo'
};
sinon.stub(myObj, 'prop').get(function getterFn() {
return 'bar';
});
myObj.prop; // 'bar'
But how can I do the same with Jest?
I can't just overwrite the function with something like jest.fn(), because it won't replace the getter
"can't set the value of get"
For anyone else stumbling across this answer, Jest 22.1.0 introduced the ability to spy on getter and setter methods.
Edit: like in scieslak's answer below, because you can spy on getter and setter methods, you can use Jest mocks with them, just like with any other function:
class MyClass {
get something() {
return 'foo'
}
}
jest.spyOn(MyClass.prototype, 'something', 'get').mockReturnValue('bar')
const something = new MyClass().something
expect(something).toEqual('bar')
You could use Object.defineProperty
Object.defineProperty(myObj, 'prop', {
get: jest.fn(() => 'bar'),
set: jest.fn()
});
If you care about spying only, go for #Franey 's answer. However if you actually need to stub a value for the getter, this is how you can do it:
class Awesomeness {
get isAwesome() {
return true
}
}
describe('Awesomeness', () => {
it('is not always awesome', () => {
const awesomeness = new Awesomeness
jest.spyOn(awesomeness, 'isAwesome', 'get').mockReturnValue(false)
expect(awesomeness.isAwesome).toEqual(false)
})
})
In my unity test case, as it is expected to mock external dependencies, following the thomaux's answer, i had to use a real instance instead of a mocked one in beforeEach function, see the snippet bellow.
//declaration of MyExternalConfigService with getter
#Injectable()
export class MyExternalConfigService {
constructor(private readonly configService: ConfigService) {}
get myProp():string {
return this.configService.get<string>('context.myProp')
}
}
//beforeEach unit test configuration
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
MyServiceToBeTested,
{
provide: MyExternalConfigService,
useValue: new MyExternalConfigService(new ConfigService())
}
]
}).compile()
service = module.get<MyServiceToBeTested>(
MyServiceToBeTested
)
configService = module.get<MyExternalConfigService>MyExternalConfigService)
})
//mocking a value to your test case
it('my test case', ()=>{
jest
.spyOn(configService, 'myProp', 'get')
.mockImplementationOnce(() => 'mockedValue')
...
)

How to reset state before each test in jest?

I have following classes and files
//A.ts
export abstract class A{
protected abstract method1(arg){
}
}
// C.ts
export interface C{
public methodC1(){
};
}
//CImpl.ts
export class CImpl implements C{
public methodC1(){
// do something
};
}
// B.ts
import appConfig from 'config';
export class B extends A{
private _enable : boolean appConfig.get('IsEnabledC');
private init(argC:C | undefined)
{
if(this._enable){
argC = new CImpl();
}
argC.methodC1();
return argC;
}
private func(argC){
let resource = getResource();
switch(resource.status){
case 1:{
throw(some exception);
}
case 2:{
throw(another exception);
}
default:{
// do something
}
}
private cleanup(){
//do something;
}
protected async method1(arg){
let argC: C | undefined;
try{
argC = init(argC);
await this.func(argC);
}
catch(exception){
this.cleanup();
}
}
}
// B.spec.ts
describe("B test", () => {
it("check if cleanup is called on resource status 1", () => {
const varB = new B();
const spied = jest.spyOn(B.prototype as any,"init").mockReturnValue(new CImpl(argC));
const spied2 = jest.spyOn(B.prototype as any,"cleanup").mockImplementation(()=>{console.log("cleanup got called")});
await varB["method1"](arg);
expect(spied).toBeCalledTimes(1);
expect(spied2).toBeCalledTimes(1);
});
it("check if cleanup is called on resource status 2", () => {
const varB = new B();
const spied = jest.spyOn(B.prototype as any,"init").mockReturnValue(new CImpl(argC));
const spied2 = jest.spyOn(B.prototype as any,"cleanup").mockImplementation(()=>{console.log("cleanup got called")});
await varB["method1"](arg);
expect(spied).toBeCalledTimes(1);
expect(spied2).toBeCalledTimes(1);
});
})
I want to test two status viz. 1 and 2 for resource inside func method. I have also mocked the getResource() method and setting status 1 and 2 in test respectively. I want that cleanup method gets called only once in each test.
But eventually in second test both spied and spied2 are getting called 2 times.
When I am running each test separately then they are working fine, but when they are run together first test is getting passed and second test is getting failed in expect assertion with message jest.fn() is called 2 times.
How can I reset state after/before each test?
you can use these methods: beforeAll, afterAll, beforeEach, afterEach.
https://jestjs.io/docs/setup-teardown
for clearing mocks you can use .mockClear()
https://jestjs.io/docs/mock-function-api#mockfnmockclear
upd:
You can use it only on mocked functions. It can be called anywhere in a test, but you'll probably need it inside afterAll. It will look like this:
import { yourMockedFunction } from '...'
afterAll(() => {
yourMockedFunction.clearMock();
});
// your tests here

Jest spyOn a function not Class or Object type

I am familiar with setting spies on Class or Object methods, but what about when the function is just an export default - such that the method itself is independent, like a utility?
I have some existing code like so:
const Funct1 = props => {
if(props){
Funct2(args);
}
// or return something
};
const Funct2 = props => {
// do something
return true
};
export default Funct1; //Yes the existing export is named the same as the "entry" method above.
And, for example, I'd like to spy on Funct1 getting called and Funct2 returns true.
import Funct1 from "../../../src/components/Funct1";
describe("Test the Thing", () => {
it("New Test", () => {
let props = {
active: true,
agentStatus: "online"
};
const spy = spyOn(Funct2, "method name"); <-- how doe this work if not an obj or class?
Funct1(props);
//If I try Funct2(props) instead, terminal output is "Funct2 is not defined"
expect(spy).toHaveBeenCalledWith(props);
});
});
I am not expert in jest, but my recommendation to think about:
1) When the function is exported as default I use something like:
import Funct1 from "../../../src/components/Funct1";
...
jest.mock("../../../src/components/Funct1");
...
expect(Funct1).toHaveBeenCalledWith(params);
2) When the module (utils.js) has multiple exports as
export const f1 = () => {};
...
export const f8 = () => {};
You can try
import * as Utils from "../../../src/components/utils"
const f8Spy = jest.spyOn(Utils, 'f8');
...
expect(f8Spy).toHaveBeenCalledWith(params);
Similar discussion here
Wrap your function with jest.fn. Like this:
const simpleFn = (arg) => arg;
const simpleFnSpy = jest.fn(simpleFn);
simpleFnSpy(1);
expect(simpleFnSpy).toBeCalledWith(1); // Passes test
I think Jest requires mocks to be inside an Object or Class, but if they aren't like that in your code, you can still put them there in the test:
jest.mock('../pathToUtils', () => ({
func2: jest.fun().mockImplementation((props) => "ok") //return what you want
otherfuncs: ...etc
}));
const mockUtils = require('../pathToUtils')
const func1 = require('./pathToFunc1')
Then in the test:
func1() // call the main function
expect(mockUtils.func2).toHaveBeenCalled
expect(mockUtils.func2).toHaveBeenCalledTimes(1)
expect(mockUtils.func2).toHaveBeenCalledWith(arg1, arg2, etc)
expect(mockUtils.func2).toHaveBeenLastCalledWith(arg1, arg2, etc)
You'll have already done unit testing of the Util function in another file, so you won't actually be running the function again here, this is just mocking for the purpose of the integration testing.
I believe that is not possible to test Funct1 calling Funct2 without modifying the existing code. If the latter is an option, here are two options to introduce dependency injection:
Create and export a factory function:
const Funct2 = props => {
// do something
return true;
};
const Funct1 = CreateFunct1(Funct2);
export function CreateFunct1(Funct2) {
return props => {
if (props) {
Funct2(props);
}
// or return something
};
}
export default Funct1;
// and here is the test:
describe('Test the Thing', () => {
it('New Test', () => {
// Arrange
const funct2Spy = jasmine.createSpy('Funct2');
const funct1 = CreateFunct1(funct2Spy);
const props = "some data";
// Act
funct1(props);
// Assert
expect(funct2Spy).toHaveBeenCalledWith(props);
});
});
Export Funct2 too. Here is a thread on this topic. Maybe its example would have to be tweaked a little bit for your scenario because of the export syntax.

How to mock/replace getter function of object with Jest?

In Sinon I can do the following:
var myObj = {
prop: 'foo'
};
sinon.stub(myObj, 'prop').get(function getterFn() {
return 'bar';
});
myObj.prop; // 'bar'
But how can I do the same with Jest?
I can't just overwrite the function with something like jest.fn(), because it won't replace the getter
"can't set the value of get"
For anyone else stumbling across this answer, Jest 22.1.0 introduced the ability to spy on getter and setter methods.
Edit: like in scieslak's answer below, because you can spy on getter and setter methods, you can use Jest mocks with them, just like with any other function:
class MyClass {
get something() {
return 'foo'
}
}
jest.spyOn(MyClass.prototype, 'something', 'get').mockReturnValue('bar')
const something = new MyClass().something
expect(something).toEqual('bar')
You could use Object.defineProperty
Object.defineProperty(myObj, 'prop', {
get: jest.fn(() => 'bar'),
set: jest.fn()
});
If you care about spying only, go for #Franey 's answer. However if you actually need to stub a value for the getter, this is how you can do it:
class Awesomeness {
get isAwesome() {
return true
}
}
describe('Awesomeness', () => {
it('is not always awesome', () => {
const awesomeness = new Awesomeness
jest.spyOn(awesomeness, 'isAwesome', 'get').mockReturnValue(false)
expect(awesomeness.isAwesome).toEqual(false)
})
})
In my unity test case, as it is expected to mock external dependencies, following the thomaux's answer, i had to use a real instance instead of a mocked one in beforeEach function, see the snippet bellow.
//declaration of MyExternalConfigService with getter
#Injectable()
export class MyExternalConfigService {
constructor(private readonly configService: ConfigService) {}
get myProp():string {
return this.configService.get<string>('context.myProp')
}
}
//beforeEach unit test configuration
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
MyServiceToBeTested,
{
provide: MyExternalConfigService,
useValue: new MyExternalConfigService(new ConfigService())
}
]
}).compile()
service = module.get<MyServiceToBeTested>(
MyServiceToBeTested
)
configService = module.get<MyExternalConfigService>MyExternalConfigService)
})
//mocking a value to your test case
it('my test case', ()=>{
jest
.spyOn(configService, 'myProp', 'get')
.mockImplementationOnce(() => 'mockedValue')
...
)

Test constructor calling a method with Sinon

I'd like to ensure that the constructor is calling a method when instantiated with Sinon, however, I can't seem to get this to work, as I believe the sinon is not watching the correct instantiation:
class Test {
constructor() {
this.someFunction();
}
someFunction() {
return 1;
}
}
... and the test
describe('constructor', () => {
it('should call someFunction()', () => {
const spyFunc = new Spy(new Test(), 'someFunction');
expect(spyFunc.calledOnce).to.be.true;
});
});
Try to spy to Test.prototype.someFunction before invoking constructor.
Something like this
sinon.spy(Test.prototype, 'someFunction')
const spyFunc = new Test();
expect(spyFunc.someFunction.calledOnce).to.be.true;

Categories