How to spy on an imported function using Sinon? - javascript

Let's say we want to test that a specific function is called by another function using Sinon.
fancyModule.js
export const fancyFunc = () => {
console.log('fancyFunc')
}
export default const fancyDefault = () => {
console.log('fancyDefault')
fancyFunc()
}
fancyModule.test.js
import sinon from 'sinon'
import fancyDefault, { fancyFunc } from '../fancyModule'
describe('fancyModule', () => {
it('calls fancyFunc', () => {
const spy = sinon.spy(fancyFunc)
fancyDefault()
expect(spy.called).to.be.true
})
})
When I run this test the actual value is always false. Also, the original function fancyFunc() gets invoked (outputs fancyFunc) instead of being mocked.

You can change the import style, and import your module as an Object like this
import sinon from 'sinon'
import * as myModule from '../fancyModule'
describe('fancyModule', () => {
it('calls fancyFunc', () => {
const spy = sinon.spy(myModule, 'fancyFunc');
myModule.fancyDefault()
expect(spy.called).to.be.true
})
})

You should use https://github.com/speedskater/babel-plugin-rewire/
import sinon from 'sinon'
import fancyDefault, { __RewireAPI__ } from '../fancyModule'
describe('fancyModule', () => {
it('calls fancyFunc', () => {
const spy = sinon.spy()
__RewireAPI__.__Rewire__('fancyFunc', spy)
fancyDefault()
expect(spy.called).to.be.true
})
})
Also, check example: https://github.com/speedskater/babel-plugin-rewire#test-code-2

Related

Jest mock - how to spy on a function, within a function, within a hook within an ES module?

I have this module that I want to mock:
import { createContext, useContext } from 'react'
const SomeContext = createContext({
set: () => null,
reset: () => null,
})
export const useSomeContext = () => useContext(SomeContext)
... and here is how it is used in a custom react hook:
import { useSomeContext } from './someContext'
export const useCustomHook = () => {
const { reset, set } = useSomeContext()
useEffect(() => {
set()
return reset
}, [reset, set])
...
}
I am trying to test that the function set is called when the useCustomHook component renders. Here is my attempt at writing such a test:
import * as useSomeContextModule from '../someContext'
import { useCustomHook } from '../useCustomHook'
const mockReset = jest.fn()
const mockSet = jest.fn()
jest.mock('../someContext', () => {
const originalModule = jest.requireActual('../someContext')
return {
__esModule: true,
...originalModule,
useSomeContext: jest.fn(() => ({
reset: mockReset,
set: mockSet,
})),
}
})
test('set method is called when useCustomHook renders', () => {
useCustomHook()
expect(mockSet).toHaveBeenCalled()
})
But, i get this error:
TypeError: Cannot destructure property 'reset' of '(0 , _index.useSomeContext)(...)' as it is undefined.
7 |
8 | export const useCustomHook = () => {
> 9 | const { reset, set } = useSomeContext()
Instead of using jest.mock I used jest.spyOn:
import * as useSomeContextModule from '../someContext'
import { useCustomHook } from '../useCustomHook'
import { renderHook, cleanup } from '#testing-library/react-hooks'
const mockReset = jest.fn()
const mockSet = jest.fn()
beforeEach(() => {
jest.spyOn(useSomeContextModule, 'useSomeContext').mockReturnValue({
reset: mockReset,
set: mockSet,
})
})
test('set method is called when useCustomHook renders', () => {
renderHook(() => useCustomHook())
expect(mockSet).toHaveBeenCalledOnce()
})
test('reset method is called when useCustomHook unmounts', async () => {
renderHook(() => useCustomHook())
await cleanup()
expect(mockReset).toHaveBeenCalledOnce()
})

How to test if an imported ES6 function is called on useEffect inside the component?

I'm trying to create a simple test in Jest that should check if an imported function is called inside useEffect in the component's lifecycle.
index.js
import { myFunc } from './myFunctions';
const myComponent = () => {
useEffect(() => {
myFunc()
}, [])
return ()
}
export default MyComponent
And now I want to create a test index.test.js file to test if that myFunc was called one time. I've got a special function that renders the mocked component (it's called prepare()).
index.test.js
import * as myFunctions from './myFunctions';
describe('when the component renders', () => {
it('the function should be called once', () => {
const spy = jest.spyOn(myFunctions, 'myFunc');
prepare(); // Function rendering myComponent
expect(spy).toHaveBeenCalledTimes(1);
});
myFunctions.js
export const myFunc = () => {
doSomething()
}
...
My error
expect(jest.fn()).toHaveBeenCalledTimes(expected)
Expected number of calls: 1
Received number of calls: 0

Unable to mock react hooks with jest

I'm using Jest to test a file written in react, I'm trying to mock the hooks but for some reason I still fail. I've tried to follow several suggestions (included stackoverflow answers) but nothing is working. I've tried to simplify the code to find my mistake but it still failing.
// myFileToTest.js
import { useContext, useState } from 'react';
export const returnsUseContext = () => {
return useContext;
};
export const returnsUseState = () => {
return useState;
};
// myFileToTest.test.js
import React from 'react';
import { returnsUseContext, returnsUseState } from './myFileToTest';
describe('test 1', () => {
let realUseContext;
let useContextMock;
beforeEach(() => {
realUseContext = React.useContext;
useContextMock = React.useContext = jest.fn();
});
// Cleanup mock
afterEach(() => {
React.useContext = realUseContext;
});
it('first try', () => {
expect(returnsUseContext()).toBe(useContextMock);
});
});
describe('test 2', () => {
beforeEach(() => {
jest.spyOn(React, 'useState').mockReturnValue({
name: 'hello'
});
});
it('second try', () => {
const useStateMock = jest.spyOn(React, 'useState');
expect(returnsUseState()).toBe(useStateMock);
});
});
and are both failing. Am I making any silly mistakes?

how to check axios get function called or not in react js?

I am trying to test below function or in other words I am trying to write unit test cases of below function.But I am getting error _axios.default.get.mockResolvedValueOnce is not a function
import React from "react";
import axios from "axios";
export default () => {
const [state, setState] = React.useState([]);
const fetchData = async () => {
const res = await axios.get("https://5os4e.csb.app/data.json");
setState(res.data);
};
React.useEffect(() => {
(async () => {
await fetchData();
})();
}, []);
return [state];
};
here is my code
https://codesandbox.io/s/awesome-jepsen-5os4e?file=/src/usetabData.test.js
I write unit test case like that
import useTabData from "./useTabData";
import { act, renderHook, cleanup } from "#testing-library/react-hooks";
import mockAxios from "axios";
describe("use tab data", () => {
afterEach(cleanup);
it("fetch tab data", async () => {
mockAxios.get.mockResolvedValueOnce({
data: {
name: "hello"
}
});
await act(async () => renderHook(() => useTabData()));
expect(mockAxios.get).toHaveBeenCalled();
});
});
Code sandbox doesn't support manual mocks as far as I know.
However, your __mock__ is placed in wrong directory structure. It should be a sibling of node_module.
Having said that, easiest way is to use https://github.com/ctimmerm/axios-mock-adapter
import useTabData from "./useTabData";
import { act, renderHook, cleanup } from "#testing-library/react-hooks";
import axios from "axios";
import MockAxiosAdapter from "axios-mock-adapter";
const mockAxios = new MockAxiosAdapter(axios);
afterEach(() => {
cleanup();// this is not needed . its done by testing library after each.
mockAxios.reset();
});
describe("use tab data", () => {
it("fetch tab data", async () => {
mockAxios.onGet(200, { data: { test: "123" } }); // response code and object.
const { result, waitForNextUpdate } = renderHook(() => useTabData());
const [value] = result.current;
// assert value
// assert the axios call by using history object
});
});
You can use history to assert: https://github.com/ctimmerm/axios-mock-adapter#history

How to spy on a function called inside class object

I want to test if this.service.someMethod is called using jasmine spy.
Source file:
// src.ts
import { Service } from 'some-package';
export class Component {
service = new Service();
callMethod() {
this.service.thatMethod();
}
}
Spec file:
// src.spec.ts
import { Component } from './src';
describe('test', () => {
it('calls thatMethod of service', () => {
let comp = new Component();
spyOn(comp.service, 'thatMethod').and.callThrough();
comp.callMethod();
expect(comp.service.thatMethod).toHaveBeenCalled();
});
});
Output:
Failed test: Expected comp.service.thatMethod to have been called.
I would suggest you to refactor your code and take advantage of IoC (inversion of control) pattern. That means that you have to get rid of Service dependency in your Component class and inject it manually, like this:
export class Component {
constructor(service) {
this.service = service;
}
callMethod() {
this.service.thatMethod();
}
}
// Elsewhere in your code
import { Service } from 'some-package';
const component = new Component(new Service());
This approach will allow you to test your components effectively with Service mock:
import { Component } from './src';
describe('test', () => {
it('calls thatMethod of service', () => {
const service = jasmine.createSpyObj('service', ['thatMethod']);
let comp = new Component(service);
comp.callMethod();
expect(service.thatMethod).toHaveBeenCalled();
});
});

Categories