React mock Graphql subscription - reactjs

I have a react project, which I am testing using Jest. I am trying to mock a call to AWS graphql and in particular, a subscribe. Here is the code I am trying to mock
await API.graphql(
graphqlOperation(subscriptions.addedProduct))
.subscribe({
next: (response) => {
this.setState({ products: [...this.state.products, response.value.data.addedProduct] })
}
});
I usually mock using spyOn with something like this...
const mockProducts = jest.spyOn(API, 'graphql').mockImplementation(async () =>
Promise.resolve(mockResponse)
);
but would get the error
TypeError: _awsAmplify.API.graphql(...).subscribe is not a function
Has anyone got an example of mocking a subscribe in a similar way?

You need to mock the return value of API.graphql as an Observable that come from reactive programming. Then, you can use .subscribe method. Below example, I use the of operator of rxjs to create an Observable.
E.g.
main.jsx:
import React, { Component } from 'react';
import { API } from './api';
export class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
products: [],
};
}
async componentDidMount() {
await API.graphql('graphqlOperation(subscriptions.addedProduct)').subscribe({
next: (response) => {
this.setState({ products: [...this.state.products, response.value.data.addedProduct] });
},
});
}
render() {
return <div>my component</div>;
}
}
main.test.js:
import { MyComponent } from './main';
import { API } from './api';
import { of } from 'rxjs';
describe('61454572', () => {
it('should pass', async () => {
const mockResponse = { value: { data: { addedProduct: 'fake product' } } };
const graphqlSpy = jest.spyOn(API, 'graphql').mockImplementation(() => {
return of(mockResponse);
});
const wrapper = shallow(<MyComponent></MyComponent>);
expect(wrapper.state('products')).toEqual(['fake product']);
expect(graphqlSpy).toBeCalledWith('graphqlOperation(subscriptions.addedProduct)');
graphqlSpy.mockRestore();
});
});
unit test results with coverage report:
PASS stackoverflow/61454572/main.test.jsx (11.328s)
61454572
✓ should pass (12ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 88.24 | 100 | 83.33 | 86.67 |
api.js | 50 | 100 | 0 | 50 | 5-6
main.jsx | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 13.119s

Related

Jest React: How to mock window eventListener set in componentDidMount

Here I need to add tests for handleBeforeUnload in window eventListener but I am getting error, How will I resolve it?
expect(jest.fn())[.not].toHaveBeenCalled()
Matcher error: received value must be a mock or spy function
Received has value: undefined
map.beforeunload();
expect(wrapper.handleBeforeUnload).toHaveBeenCalled();
My component
componentDidMount() {
window.addEventListener('beforeunload', (event) => {
this.handleBeforeUnload();
});
}
handleBeforeUnload () {
}
My test spec:
it('should call handleBeforeUnload', () => {
const historyMock = { listen: jest.fn(), replace: jest.fn() };
const map = {};
window.addEventListener = jest.fn((event, cb) => {
map[event] = cb;
});
const wrapper = shallow(
<AppRoutes history={historyMock} getDctkConfig={getDctkConfig} />,
);
map.beforeunload();
expect(wrapper.handleBeforeUnload).toHaveBeenCalled();
});
You didn't spy the.handleBeforeUnload() method of the component class. You can spy it through Component.prototype.handleBeforeUnload. Besides, you can mock the implementation of the .addEventListener() method and invoke the listener function manually.
index.tsx:
import React, { Component } from 'react';
export default class AppRoutes extends Component {
componentDidMount() {
window.addEventListener('beforeunload', (event) => {
this.handleBeforeUnload();
});
}
handleBeforeUnload() {}
render() {
return <div>app routes</div>;
}
}
index.test.tsx:
import { shallow } from 'enzyme';
import React from 'react';
import AppRoutes from './';
describe('69346085', () => {
afterEach(() => {
jest.restoreAllMocks();
});
test('should pass', () => {
const handleBeforeUnloadSpy = jest.spyOn(AppRoutes.prototype, 'handleBeforeUnload');
jest
.spyOn(window, 'addEventListener')
.mockImplementation((type: string, listener: EventListenerOrEventListenerObject) => {
typeof listener === 'function' && listener({} as Event);
});
shallow(<AppRoutes />);
expect(handleBeforeUnloadSpy).toHaveBeenCalled();
});
});
test result:
PASS examples/69346085/index.test.tsx (9.191 s)
69346085
✓ should pass (7 ms)
-----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.tsx | 100 | 100 | 100 | 100 |
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 9.814 s, estimated 10 s

React testing axios get request in componentDidMount using jest

I am new to React testing and I am trying to test a get request which is coming from a backend call using axios.
The component:
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
stockData: [],
}
}
componentDidMount() {
axios.get("http://localhost:8080/stocks")
.then(response => {
this.setState({
stockData: response.data
})
})
}
render() {
return (
<ChildComponent stockData={this.state.stockData}/>
)
}
}
The data fetched from the call is something like the following:
stockData: [
{
"ticker": "AAPL",
"name": "Apple Inc",
"priceChanges": {
"daily": 1.55,
"weekly": -3.55,
"monthly": -20.00
},
"financialData": {
"roa": 5.74,
"roe": 20.07,
"market_cap": "1.2T"
}
},
{
"ticker": "MSFT",
"name": "Microsoft Corporation",
"priceChanges": {
"daily": 4.35,
"weekly": 1.25,
"monthly": -22.05
},
"financialData": {
"roa": 8.73,
"roe": 15.07,
"market_cap": "1.3T"
}
}
//and many other similar objects
]
I have read that I should not really use the real get request and instead use some "mock" data but don't have an idea how to implement it. I have tried the following, but I am not sure if it is the right way to do this. Also, I am getting an error: TypeError: Cannot read property 'then' of undefined
test('should fetch company', () => {
const wrapper = shallow(<ParentComponent/>);
const resp = {stockData: [
{
"ticker": "AAPL",
"name": "Apple Inc",
"priceChanges": {
"daily": 1.55,
"weekly": -3.55,
"monthly": -20.00
},
"financialData": {
"roa": 5.74,
"roe": 20.07,
"market_cap": "1.2T"
}
}
};
wrapper.instance().componentDidMount().then(resp => {
expect(wrapper.state('stockData')).toContain(resp.stockData);
});
});
You can use jest.spyOn(object, methodName) to mock axios.get method and its resolved value. Since axios.get is an asynchronous operation, We need to wait until it is done.
E.g.
parent.jsx:
import React from 'react';
import { ChildComponent } from './child';
import axios from 'axios';
export class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
stockData: [],
};
}
componentDidMount() {
axios.get('http://localhost:8080/stocks').then((response) => {
this.setState({ stockData: response.data });
});
}
render() {
return <ChildComponent stockData={this.state.stockData} />;
}
}
child.jsx:
import React, { Component } from 'react';
export class ChildComponent extends Component {
render() {
return <div></div>;
}
}
parent.test.jsx:
import { ParentComponent } from './parent';
import { shallow } from 'enzyme';
import axios from 'axios';
import React from 'react';
import { act } from 'react-dom/test-utils';
const whenStable = async () => {
await act(async () => {
await new Promise((resolve) => setTimeout(resolve, 0));
});
};
describe('61465031', () => {
it('should pass', async () => {
const mResponse = { data: ['a', 'b'] };
const getSpy = jest.spyOn(axios, 'get').mockResolvedValueOnce(mResponse);
const wrapper = shallow(<ParentComponent></ParentComponent>);
await whenStable();
expect(wrapper.find('ChildComponent').prop('stockData')).toEqual(['a', 'b']);
getSpy.mockRestore();
});
});
unit test results with coverage report:
PASS stackoverflow/61465031/parent.test.jsx (11.345s)
61465031
✓ should pass (20ms)
------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------|---------|----------|---------|---------|-------------------
All files | 95.45 | 100 | 85.71 | 94.12 |
child.jsx | 85.71 | 100 | 50 | 80 | 5
parent.jsx | 100 | 100 | 100 | 100 |
------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 12.876s
source code: https://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/61465031

How to mock a React Component static method with Jest

This was really hard to find out how to do. I could not find any code to show you how to mock a static method in a React Component. This way worked for me.
// YourComponent.js
class YourComponent extends Component {
static getHelloWorld () {
return 'hello world';
}
render() {
return (
<div>{YourComponent.getHelloWorld()}</div>
)
}
}
export default YourComponent;
// YourComponent.test.js
import { mount } from 'enzyme';
import YourComponent from './YourComponent';
YourComponent.__proto__.getHelloWorld = jest.fn(() => { return 'Hello Universe' });
describe('YourComponent test for mocking static method', () => {
it('should render', () => {
const wrapper = mount(<YourComponent />);
expect(wrapper.text()).toEqual('Hello Universe');
});
});
Here is the solution:
index.js:
import { Component } from 'react';
class YourComponent extends Component {
static getHelloWorld() {
return 'hello world';
}
render() {
return <div>{YourComponent.getHelloWorld()}</div>;
}
}
export default YourComponent;
index.test.js:
import { mount } from 'enzyme';
import YourComponent from './';
describe('YourComponent test for mocking static method', () => {
it('should render', () => {
YourComponent.getHelloWorld = jest.fn(() => {
return 'Hello Universe';
});
const wrapper = mount(<YourComponent />);
expect(wrapper.text()).toEqual('Hello Universe');
});
});
unit test result with coverage report:
PASS stackoverflow/61022182/index.test.js (8.455s)
YourComponent test for mocking static method
✓ should render (30ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 88.89 | 100 | 66.67 | 87.5 |
index.js | 88.89 | 100 | 66.67 | 87.5 | 5
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 10.327s

Assert onBlur() funciton in functional component jest

I wrote a test case to call onBlur method, but I'm getting an error when I try to assert it. Here is the above test case.
it("call the handlingBlurEmail method", () => {
const wrapper = mount(
<App childRef={() => {}} />
);
const comp = wrapper.find({ id: "email" }).first();
comp.prop("onBlur")({
target: { id: "email", value: "test#gmail.com" }
});
expect(
wrapper
.find("AccountForm")
.state()
.onBlur()
).toHaveBeenCalled();
});
and the function for which I'm writing test case is
mailReference = React.createRef();
handlingEmailBlur = events => {
this.mailReference.current.validate(events.target.value);
};
render = () {
......
return (
<div className="Form1">
onBlur={this.handlingEmailBlur}
</div>
)
.....
}
Please let me know how to add assert statement in order to call the onBlur() method in the above test case
Here is the unit testing solution:
index.tsx:
import React, { Component } from 'react';
class App extends Component {
mailReference = React.createRef();
handlingEmailBlur = (events) => {
this.mailReference.current.validate(events.target.value);
};
render() {
return (
<div className="Form1" onBlur={this.handlingEmailBlur}>
some component
</div>
);
}
}
export default App;
index.spec.tsx:
import App from './index';
import { mount } from 'enzyme';
import React from 'react';
describe('59455504', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('call the handlingBlurEmail method', () => {
const mailReference = { current: { validate: jest.fn() } };
jest.spyOn(React, 'createRef').mockReturnValue(mailReference);
const wrapper = mount(<App childRef={() => {}} />);
const mEvent = {
target: { id: 'email', value: 'test#gmail.com' },
};
wrapper.find('.Form1').prop('onBlur')(mEvent);
expect(mailReference.current.validate).toBeCalledWith(mEvent.target.value);
});
});
Unit test result with 100% coverage:
PASS src/stackoverflow/59455504/index.spec.jsx (8.328s)
59455504
✓ call the handlingBlurEmail method (40ms)
-----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.jsx | 100 | 100 | 100 | 100 | |
-----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 9.769s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59455504

How to test redirects in ComponentWillMount in enzyme/jest

I am new to testing in Enzyme and Jest.
I want to test my ComponentWillMount method, whether certain redirect will happen or not.
Here is the code of my component:
class ActivationSF extends Component {
constructor(props) {
super(props);
this.className = 'ActivationSF.js'
this.state = {
messages: null,
}
}
render() {
return (
<ActivationUI
context={this.props.context}
messages={this.state.messages}
/>
);
}
componentWillMount() {
let context = this.props.context
if(!context.userInfo){
window.location.replace('/identify')
}
let externalLP = ExternalLandingPageUtil.getExternalLandingPageUrl(context);
if (externalLP) {
window.location.replace(`${externalLP}`);
return;
}
if (context.userInfo)
{
window.location = '/store'
}
}
componentDidMount() {
document.body.classList.add('sdp')
}
Here is a unit test strategy:
index.tsx:
import React, { Component } from 'react';
import { ExternalLandingPageUtil } from './ExternalLandingPageUtil';
import { ActivationUI } from './ActivationUI';
export class ActivationSF extends Component<any, any> {
public className: string;
constructor(props) {
super(props);
this.className = 'ActivationSF.js';
this.state = {
messages: null
};
}
render() {
return <ActivationUI context={this.props.context} messages={this.state.messages} />;
}
componentWillMount() {
let context = this.props.context;
if (!context.userInfo) {
window.location.replace('/identify');
}
let externalLP = ExternalLandingPageUtil.getExternalLandingPageUrl(context);
if (externalLP) {
window.location.replace(`${externalLP}`);
return;
}
if (context.userInfo) {
window.location.replace('/store');
}
}
componentDidMount() {
document.body.classList.add('sdp');
}
}
ActivationUI.tsx:
import React from 'react';
export const ActivationUI = ({ context, messages }) => <div>ActivationUI</div>;
ExternalLandingPageUtil.ts:
export const ExternalLandingPageUtil = {
getExternalLandingPageUrl(context) {
return context.externalLP;
}
};
index.spec.tsx:
import React from 'react';
import { ActivationSF } from './';
import { ExternalLandingPageUtil } from './ExternalLandingPageUtil';
import { shallow } from 'enzyme';
window.location.replace = jest.fn();
describe('ActivationSF', () => {
afterEach(() => {
jest.resetAllMocks();
});
test('should redirect to /identify', () => {
const mProps = { context: {} };
const wrapper = shallow(<ActivationSF {...mProps}></ActivationSF>);
expect(window.location.replace).toBeCalledWith('/identify');
});
test('should redirect to external IP address', () => {
const spy = jest.spyOn(ExternalLandingPageUtil, 'getExternalLandingPageUrl');
const mProps = { context: { userInfo: {}, externalLP: '10.0.0.1' } };
const wrapper = shallow(<ActivationSF {...mProps}></ActivationSF>);
expect(window.location.replace).toBeCalledWith('10.0.0.1');
expect(spy).toBeCalledWith(mProps.context);
});
test('should redirect to /store', () => {
const mProps = { context: { userInfo: {} } };
const wrapper = shallow(<ActivationSF {...mProps}></ActivationSF>);
expect(window.location.replace).toBeCalledWith('/store');
});
test('should render correctly', () => {
const mProps = { context: {} };
const addSpy = jest.spyOn(document.body.classList, 'add');
const wrapper = shallow(<ActivationSF {...mProps}></ActivationSF>);
expect(wrapper.prop('context')).toEqual(mProps.context);
expect(wrapper.state('messages')).toBeNull();
expect(addSpy).toBeCalledWith('sdp');
addSpy.mockRestore();
});
});
Unit test result with 100% coverage for ActivationSF component.
PASS src/stackoverflow/57673447/index.spec.tsx (12.286s)
ActivationSF
✓ should redirect to /identify (14ms)
✓ should redirect to external IP address (2ms)
✓ should redirect to /store (1ms)
✓ should render correctly (2ms)
----------------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------------------------|----------|----------|----------|----------|-------------------|
All files | 90.32 | 100 | 85.71 | 100 | |
ActivationUI.tsx | 40 | 100 | 0 | 100 | |
ExternalLandingPageUtil.ts | 100 | 100 | 100 | 100 | |
index.tsx | 100 | 100 | 100 | 100 | |
----------------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 4 passed, 4 total
Snapshots: 0 total
Time: 14.412s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/57673447

Resources