Jest mock async function from default import - javascript

I'm trying to mock an async function that is exported as a default export but all I get is TypeError: Cannot read property 'then' of undefined
What I'm trying to mock is config.js:
const configureEnvironment = async (nativeConfig) => {
return { await whatever() }
}
The file I'm testing is Scene.js:
import configureEnvironment from './config';
class Scene extends React.Component {
constructor(props) {
nativeConfig = {};
configureEnfironment(nativeConfig).then((config) => {
// Do stuff
}
}
}
And my test file is Scene.test.js:
let getScene = null;
const configureEnvironmentMock = jest.fn();
describe('Scene', () => {
jest.mock('./config', () => configureEnvironmentMock);
const Scene = require('./Scene').default;
getScene = (previousState) => {
return shallow(
<Scene prevState={previousState}>
<Fragment />
</Scene>,
);
};
it('calls configureEnvironment with the nativeConfig', async () => {
expect.assertions(1);
const nativeConfig = {};
getScene(nativeConfig);
expect(configureEnvironmentMock).toHaveBeenCalledWith(nativeConfig);
});
});
However, the result of running the test is:
TypeError: Cannot read property 'then' of undefined
I understand the issue is on the way I mock configureEnvironment but I cannot get it working.
I also tried to mock the function like:
jest.mock('./config', () => {
return {
default: configureEnvironmentMock,
};
});
But it results on:
TypeError: (0 , _config2.default) is not a function

A clean and simple way to mock the default export of a module is to use jest.spyOn in combination with functions like mockImplementation.
Here is a working example based on the code snippets above:
config.js
const whatever = async () => 'result';
const configureEnvironment = async (nativeConfig) => await whatever();
export default configureEnvironment;
Scene.js
import * as React from 'react';
import configureEnvironment from './config';
export class Scene extends React.Component {
constructor(props) {
super(props);
configureEnvironment(props.prevState).then((config) => {
// Do stuff
});
}
render() {
return null;
}
}
Scene.test.js
import React, { Fragment } from 'react';
import { shallow } from 'enzyme';
import { Scene } from './Scene';
import * as config from './config';
describe('Scene', () => {
const mock = jest.spyOn(config, 'default'); // spy on the default export of config
mock.mockImplementation(() => Promise.resolve('config')); // replace the implementation
const getScene = (previousState) => {
return shallow(
<Scene prevState={previousState}>
<Fragment />
</Scene>,
);
};
it('calls configureEnvironment with the nativeConfig', async () => {
expect.assertions(1);
const nativeConfig = {};
getScene(nativeConfig);
expect(mock).lastCalledWith(nativeConfig); // SUCCESS
});
});

You can mock anything with jest, like this
jest.mock('#material-ui/core/withWidth', () => ({
__esModule: true,
isWidthUp: jest.fn((a, b) => true),
default: jest.fn(fn => fn => fn)
}))

Related

TypeError: Cannot read property 'products' of undefined Mobx React Native

Mobx giving me this error, and I don't know what is going on, there is no error in my code, what I make wrong? I tried so much fix this problem, hours and hours, someone knows what wrong I do??
stores/index.ts
import ProductStore from './product.store';
class RootStore {
products: ProductStore;
constructor() {
this.products = new ProductStore();
}
}
const store = new RootStore();
export {RootStore, ProductStore};
export default store;
stores/product.store.ts
import {WalmartApi} from '../api';
import {action, makeAutoObservable, runInAction} from 'mobx';
export default class ProductStore {
products = [];
constructor() {
makeAutoObservable(this);
}
#action
getProducts = async (searchQuery: string): Promise<void> => {
const snapshot = await WalmartApi.getProductsBySegment(searchQuery);
console.log({snapshot});
runInAction(() => {
this.products = snapshot;
});
};
}
hooks/userStores.tsx
import React from 'react';
import {MobXProviderContext} from 'mobx-react';
import store from '#stores';
export const useStores = (): typeof store => {
return React.useContext(MobXProviderContext).rootStore;
};
screens/Home.tsx
const {products} = useStores();
const onInit = () => {
products.getProducts('Playstation5');
};
useEffect(() => {
onInit();
});

Typescript, React & Socket.io-client Tests

I'm trying to figure out how to write a Typescript/React app which uses socket.io to communicate to a server and thus other clients. However I'd like to write some tests in doing so.
In my sample app I have:
import io, { Socket } from 'socket.io-client';
const App = () => {
let socket: Socket;
const ENDPOINT = 'localhost:5000';
const join = (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
event.preventDefault();
socket = io(ENDPOINT);
socket.emit('join', { name: 'Paola', room: '1' }, () => {});
};
return (
<div className="join-container">
<button className="join-button" onClick={join} data-testid={'join-button'}>
Sign in
</button>
</div>
);
};
export default App;
And my test looks like:
import App from './App';
import { render, screen, fireEvent } from '#testing-library/react';
import 'setimmediate';
describe('Join', () => {
let mockEmitter = jest.fn();
beforeEach(() => {
jest.mock('socket.io-client', () => {
const mockedSocket = {
emit: mockEmitter,
on: jest.fn((event: string, callback: Function) => {}),
};
return jest.fn(() => {
return mockedSocket;
}
);
});
});
afterEach(() => {
jest.clearAllMocks();
});
it('joins a chat', () => {
// Arrange
render(<App />);
const btn = screen.getByTestId('join-button');
// Act
fireEvent.click(btn);
// Assert
expect(btn).toBeInTheDocument();
expect(mockEmitter).toHaveBeenCalled();
});
});
I just want to make sure I can mock socket.io-client so that I can verify that messages are being sent to the client and that it (later) reacts to messages sent in.
However the test is failing and it doesn't seem to be using my mock.
Error: expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
In the manual-mocks#examples doc, there is a note:
Note: In order to mock properly, Jest needs jest.mock('moduleName') to be in the same scope as the require/import statement.
So, there are two solutions:
app.tsx:
import React from 'react';
import io, { Socket } from 'socket.io-client';
const App = () => {
let socket: Socket;
const ENDPOINT = 'localhost:5000';
const join = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
socket = io(ENDPOINT);
socket.emit('join', { name: 'Paola', room: '1' }, () => {});
};
return (
<div className="join-container">
<button className="join-button" onClick={join} data-testid={'join-button'}>
Sign in
</button>
</div>
);
};
export default App;
Option 1: Call jest.mock and import ./app module in module scope of the test file.
app.test.tsx:
import App from './App';
import { render, screen, fireEvent } from '#testing-library/react';
import '#testing-library/jest-dom/extend-expect';
import React from 'react';
let mockEmitter = jest.fn();
jest.mock('socket.io-client', () => {
return jest.fn(() => ({
emit: mockEmitter,
on: jest.fn(),
}));
});
describe('Join', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('joins a chat', () => {
// Arrange
render(<App />);
const btn = screen.getByTestId('join-button');
// Act
fireEvent.click(btn);
// Assert
expect(btn).toBeInTheDocument();
expect(mockEmitter).toHaveBeenCalled();
});
});
Option 2: Since you call the jest.mock in beforeEach hook, require the './app' module in beforeEach hook function scope as well.
app.test.tsx:
import { render, screen, fireEvent } from '#testing-library/react';
import '#testing-library/jest-dom/extend-expect';
import React from 'react';
describe('Join', () => {
let mockEmitter = jest.fn();
let App;
beforeEach(() => {
App = require('./app').default;
jest.mock('socket.io-client', () => {
const mockedSocket = {
emit: mockEmitter,
on: jest.fn(),
};
return jest.fn(() => mockedSocket);
});
});
afterEach(() => {
jest.clearAllMocks();
});
it('joins a chat', () => {
// Arrange
render(<App />);
const btn = screen.getByTestId('join-button');
// Act
fireEvent.click(btn);
// Assert
expect(btn).toBeInTheDocument();
expect(fakeEmitter).toHaveBeenCalled();
});
});
package version:
"jest": "^26.6.3",
"ts-jest": "^26.4.4"
jest.config.js:
module.exports = {
preset: 'ts-jest/presets/js-with-ts',
testEnvironment: 'jsdom'
}

TypeError: moduleName.startsWith is not a function when mocking "axios"

I'm trying to mock an axios get function but I'm getting TypeError: moduleName.startsWith is not a function when mocking "axios" What would be the right way to mock it?
Error:
FAIL src/tests/Sample.test.jsx
● Test suite failed to run
TypeError: moduleName.startsWith is not a function
5 | import { backendUrl } from "../helper/constants"
6 | describe("test useEffect and axios", () => {
> 7 | const mockGet = jest.mock(axios.get)
Sample.test.jsx
import { mount } from "enzyme"
import { screen, render, act } from "#testing-library/react"
import Sample from "../pages/Sample"
import axios from "axios"
import { backendUrl } from "../helper/constants"
describe("test useEffect and axios", () => {
const mockGet = jest.mock(axios.get) // THROWS ERROR HERE
let wrapper
it("should call axios", async () => {
await act(async () => {
mockGet.mockImplementationOnce(() => Promise.resolve({}))
wrapper = mount(<Sample />)
})
wrapper.update()
await expect(mockGet).toHaveBeenCalledWith(backendUrl)
})
})
Sample.jsx
import axios from "axios"
import { useState, useEffect } from "react"
import { backendUrl } from "../helper/constants"
const Sample = () =>{
const [pets, setPets] = useState([])
useEffect(() => axios.get(backendUrl)
.then(({data}) =>setPets(data.entries))
.catch((err)=>console.log(err)), [])
return (
<>
<p>I h8 all of you</p>
{pets.map((e, i) =><h2 key={i}>{e.Link}</h2>)}
</>
)
}
export default Sample
You have to use the right syntax like this jest.mock('axios') so in your case:
jest.mock('axios')
describe("test useEffect and axios", () => {
const mockGet = jest.fn()
let wrapper
it("should call axios", async () => {
await act(async () => {
mockGet.mockImplementationOnce(() => Promise.resolve({}))
wrapper = mount(<Sample />)
})
wrapper.update()
await expect(mockGet).toHaveBeenCalledWith(backendUrl)
})
})

How to Export Axios Response on Module Export ReactJs

I need to export result of axios response on module.exports.
This is my codes:
brand.js
var axios = require('axios');
module.exports = (async function() {
try {
const { data } = axios.get('http://localhost:8000/api/v1/setting/index');
console.log(data.data.initial);
return {
name: data.data.name,
desc: data.data.description,
};
} catch (err) {
console.log(err);
}
})();
I try to import the result to be used on another file.
This is my code.
import React from 'react';
const {brand} = await require("brand.js");
class Dashboard extends Component {
render(){
const name = brand.name
const desc = brand.description;
return (
<h1>{title} | {description}</h1>
);
}
}
The result of my code is:
Can not use keyword 'await' outside an async function
This is error shown on browser:
How to solved this?
you can do like this.
// brand.js
import axios from 'axios';
export const fetchData = async () => {
let response;
try {
response = await axios.get(url, config);
} catch (e) {
// catch error
throw new Error(e.message)
}
// if success return value
return response?.data ? response?.data : null // or set initial value
}
then in your React
import { fetchData } from './path/to/fetchData';
const response = fetchData();
const MyComponent = (props) => {
return (
<div>name: {response.data.name} | desc: {response.data.description}</div>
)
}

How to Mock or SpyOn an exported function in React

I have a component that's a default export
// Component.js
export default () => <>getData()</>;
export const getData = async () => await fetch('/');
 
// Component.test.js
import Component from 'Component'
describe('test getData', () => {
const getDataMock = jest.spyOn(Component, 'getData');
expect(getDataMock).toBeCalledOnce();
})
I get error: Cannot spy the getData property because it is not a function; undefined given instead
you imported the default function instead of getData.
// Component.test.js
import mydefault, * as notdefaults from 'Component';
test('test getData', () => {
const getDataMocked = jest.spyOn(notdefaults, 'getData');
getDataMocked.mockResolvedValue('hello!');
expect(mydefault()).toBeDefined(); // call the default component to trigger getData()
expect(getDataMocked).toBeCalledTimes(1);
});

Categories

Resources