I'm creating a jQuery object inside a function that I'm testing. I need to mock the prop method call.
How can I do this? I'm using Jest. I tried with Sinon as well, but I couldn't get this working.
Here's my method:
import $ from 'jquery';
export function myFunc(element) {
var htmlControl = $(element);
var tagName = htmlControl.prop('tagName');
}
Here is the unit test solution using sinonjs:
index.ts:
import $ from 'jquery';
export function myFunc(element) {
var htmlControl = $(element);
var tagName = htmlControl.prop('tagName');
}
index.spec.ts:
import proxyquire from 'proxyquire';
import sinon from 'sinon';
import { expect } from 'chai';
describe('myFunc', () => {
it('should mock prop() method', () => {
const el = {};
const propStub = sinon.stub().returnsThis();
const jqueryStub = sinon.stub().callsFake(() => {
return {
prop: propStub
};
});
const { myFunc } = proxyquire('./', {
jquery: jqueryStub
});
myFunc(el);
expect(jqueryStub.calledWith(el)).to.be.true;
expect(propStub.calledWith('tagName')).to.be.true;
});
});
Unit test result with 100% coverage:
myFunc
✓ should mock prop() method (156ms)
1 passing (161ms)
---------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.spec.ts | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
---------------|----------|----------|----------|----------|-------------------|
Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/57461604
Related
I have a code like this
import fun from '../../../example';
export async function init (props:any){
if (fun()){
doSomething();
}
}
I'm creating unit tests for this code above, but I actually want to mock the implementation of fun in the file only as I can't alter fun in its original file
You can use jest.mock(moduleName, factory, options) to mock ../../../example module.
E.g.
index.ts:
import fun from './example';
export async function init(props: any) {
if (fun()) {
console.log('doSomething');
}
}
example.ts:
export default function fun() {
console.log('real implementation');
return false;
}
index.test.ts:
import { init } from './';
import fun from './example';
import { mocked } from 'ts-jest/utils';
jest.mock('./example', () => jest.fn());
describe('63166775', () => {
it('should pass', async () => {
expect(jest.isMockFunction(fun)).toBeTruthy();
const logSpy = jest.spyOn(console, 'log');
mocked(fun).mockReturnValueOnce(true);
await init({});
expect(logSpy).toBeCalledWith('doSomething');
expect(fun).toBeCalledTimes(1);
logSpy.mockRestore();
});
});
unit test result:
PASS stackoverflow/63166775/index.test.ts (13.298s)
63166775
✓ should pass (33ms)
console.log
doSomething
at CustomConsole.<anonymous> (node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866:25)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 50 | 100 | 100 |
index.ts | 100 | 50 | 100 | 100 | 4
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 15.261s
This is an example of my class:
class MyClass {
constructor() {
this.myLib = new MyLib()
}
myMainMethod = param => {
this.myLib.firstMethod(arg => {
arg.secondMethod(param)
})
}
}
Using Jest, how can I assert that "secondMethod" will be called with I call "myMainMethod".
MyLib is a third-party library.
Here is the unit test solution:
index.ts:
import { MyLib } from './MyLib';
export class MyClass {
myLib;
constructor() {
this.myLib = new MyLib();
}
myMainMethod = (param) => {
this.myLib.firstMethod((arg) => {
this.myLib.secondMethod(arg);
});
};
}
index.test.ts:
import { MyClass } from './';
import { MyLib } from './MyLib';
describe('60840838', () => {
it('should pass', () => {
const firstMethodSpy = jest.spyOn(MyLib.prototype, 'firstMethod').mockImplementationOnce((callback) => {
callback('arg');
});
const secondMethodSpy = jest.spyOn(MyLib.prototype, 'secondMethod').mockReturnValueOnce('fake');
const instance = new MyClass();
instance.myMainMethod('param');
expect(firstMethodSpy).toBeCalledWith(expect.any(Function));
expect(secondMethodSpy).toBeCalledWith('arg');
});
});
unit test results with coverage report:
PASS stackoverflow/60840838/index.test.ts
60840838
✓ should pass (4ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 87.5 | 100 | 71.43 | 85.71 |
MyLib.ts | 71.43 | 100 | 33.33 | 66.67 | 3,6
index.ts | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 4.54s, estimated 8s
I have a function that looks roughly like this:
import {someLibraryMethod} from 'someLibrary';
function setFullWidth(el) {
someLibraryMethod(el);
el.setAttribute('width', '100%');
}
I'm testing this function by using Sinon to create a mock element that only supports setAttribute:
const fakeElement = {
setAttribute: sinon.spy()
};
setFullWidth(fakeElement);
assert(fakeElement.setAttribute.calledWithExactly('width', '100%'));
The problem is that someLibraryMethod assumets el is an HTML element and tries to call some other methods on it, such as hasAttribute. I could add these as stubs or spies to fakeElement too, but I was wondering if Sinon has a way to create an object that will spy on any method or property access on it?
Something like:
const fakeElement = sinon.spyOnAnyPropety();
fakeElement.setAttribute(); // valid
fakeElement.hasAttribute(); // valid
fakeElement.foo(); // also valid
// some way to check which methods were called
You can use sinon.stub(obj) to
Stubs all the object’s methods.
E.g.
index.ts:
export function setFullWidth(el) {
el.setAttribute('width', '100%');
el.hasAttribute('width');
el.foo();
}
index.spec.ts:
import { setFullWidth } from './';
import sinon, { SinonStubbedInstance } from 'sinon';
import { expect } from 'chai';
describe('58868361', () => {
const fakeElement = {
setAttribute(attr, value) {
console.log('setAttribute');
},
hasAttribute(attr) {
console.log('hasAttribute');
},
foo() {
console.log('foo');
},
};
afterEach(() => {
sinon.restore();
});
it('should spy all methods of el', () => {
const logSpy = sinon.spy(console, 'log');
const stub: SinonStubbedInstance<typeof fakeElement> = sinon.stub(fakeElement);
setFullWidth(fakeElement);
expect(stub.setAttribute.calledWithExactly('width', '100%')).to.be.true;
expect(stub.hasAttribute.calledWithExactly('width')).to.be.true;
expect(stub.foo.calledOnce).to.be.true;
expect(logSpy.callCount).to.be.equal(0);
logSpy.restore();
});
it('should restore to original methods of el', () => {
const logSpy = sinon.spy(console, 'log');
setFullWidth(fakeElement);
expect(logSpy.callCount).to.be.equal(3);
});
});
Unit test result with 100% coverage:
58868361
✓ should spy all methods of el
setAttribute
hasAttribute
foo
✓ should restore to original methods of el
2 passing (13ms)
---------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.spec.ts | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
---------------|----------|----------|----------|----------|-------------------|
Source code: https://github.com/mrdulin/mongoose5.x-lab/tree/master/src/stackoverflow/58868361
I am unable to mock 3rd party function call in typescript.
The third party library is moment-timezone and I want to mock the code to get browser timezone to write jest test.
Below is the code I need to mock and return string as 'Australia/Sydney'
moment.tz.guess()
I am trying to use jest.mock() as :-
jest.mock('moment-timezone', () => () => ({ guess: () => 'Australia/Sydney' }));
Here is the solution:
index.ts:
import moment from 'moment-timezone';
export function main() {
return moment.tz.guess();
}
index.spec.ts:
import { main } from './';
import moment from 'moment-timezone';
jest.mock('moment-timezone', () => {
const mTz = {
guess: jest.fn()
};
return {
tz: mTz
};
});
describe('main', () => {
test('should mock guess method', () => {
(moment.tz.guess as jest.MockedFunction<typeof moment.tz.guess>).mockReturnValueOnce('Australia/Sydney');
const actualValue = main();
expect(jest.isMockFunction(moment.tz.guess)).toBeTruthy();
expect(actualValue).toBe('Australia/Sydney');
expect(moment.tz.guess).toBeCalled();
});
});
Unit test result with 100% coverage:
PASS src/stackoverflow/58548563/index.spec.ts
main
✓ should mock guess method (6ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 5.828s, estimated 19s
I started to write unit test cases using sinon and facing the following problem.
myfile.js
module.exports = class A{
constructor(classB_Obj){
this.classBobj = classB_Obj;
classBobj.someFunctionOfClassB(); // error coming here
}
doSomething(){
}
}
where class B is in
myfile2.js
module.exports = class B{
constructor(arg1, arg2){
this.arg1 = arg1;
this.arg2 = arg2;
}
someFunctionOfClassB(){
}
}
when I test class A and use sinon to stub Class B
const myfile2 = require('../myfile2').prototype;
const loggerStub = sinon.stub(myfile2, 'someFunctionOfClassB');
while executing it gives exception
classBobj.someFunctionOfClassB is not a function.
What is the correct way of stubbing it? I don't want to instantiate class B.
This has really nothing to do with stubbing.
You have to define this function as a static method to achieve this:
module.exports = class B{
constructor(arg1, arg2){
this.arg1 = arg1;
this.arg2 = arg2;
}
static someFunctionOfClassB(){
}
}
Then you can call the method on the class object.
When you write a normal class method you always have to instantiate the class before you can you use it on the instance:
const b = new class_Obj();
b.someFunctionOfClassB();
See also: Class vs. static method in JavaScript
Here is the unit test solution:
myfile.js:
module.exports = class A {
constructor(classB_Obj) {
this.classBobj = classB_Obj;
this.classBobj.someFunctionOfClassB();
}
doSomething() {}
};
myfile2.js:
module.exports = class B {
constructor(arg1, arg2) {
this.arg1 = arg1;
this.arg2 = arg2;
}
someFunctionOfClassB() {}
};
myfile.test.js:
const A = require("./myfile");
const B = require("./myfile2");
const sinon = require("sinon");
describe("52559903", () => {
afterEach(() => {
sinon.restore();
});
it("should pass", () => {
const bStub = sinon.createStubInstance(B, {
someFunctionOfClassB: sinon.stub(),
});
new A(bStub);
sinon.assert.calledOnce(bStub.someFunctionOfClassB);
});
});
Unit test result with coverage report:
myfile
✓ should pass
1 passing (10ms)
----------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------------|----------|----------|----------|----------|-------------------|
All files | 87.5 | 100 | 57.14 | 87.5 | |
myfile.js | 100 | 100 | 50 | 100 | |
myfile.test.js | 100 | 100 | 100 | 100 | |
myfile2.js | 33.33 | 100 | 0 | 33.33 | 3,4 |
----------------|----------|----------|----------|----------|-------------------|
Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/52559903