Introduction to the

The technology stack of the company’s front-end project is React + TS + Rxjs. React is used in large projects. One-way data flow is more accurate and consumes less memory (DIFF) than two-way binding rendering. Ts’s strong typing ensures accurate data and reduces bugs. Rxjs is better at fetching asynchronous data, independent of other elements. Technological innovation brings qualitative changes in code and efficiency, as well as impacts on some technologies. Take jEST, an old unit testing framework. Below I will describe the jEST testing challenges and solutions I encountered during the project, and I will update the blog post if I encounter new ones

1. Mock axios

Those of you who trust jest to write react unit tests should also test components with axios callback data. The normal testing process is also relatively simple, just mock axios. Similar code is as follows;

jest.mock('axios'); . axios.get.mockResolvedValue(resp);// resp = mock;
Copy the code

But what happens if TS is adopted?

what??? The strong typing of TS causes the code to fail. 1. Alias axios.get. Although this is not an error, the any type is clearly not what we want, violating the initial point of TS

(axios.get as any).mockResolvedValue(resp);
Copy the code

2. Use the callback function of jest. Mock () to satisfy. There is a certain amount of mock that can be done here, but if you use axios’s interceptor, be prepared to rewrite Axios. This approach is not recommended.

jest.mock('axios', () = > {get:(a)= >{} // This is defined as the desired mock form
})
Copy the code

All we need to do is mock the method inside the component and test it by passing out the values we want to mock. So we can use jest. SpyOn. As the name implies is our method of spying.

import axios from 'axios'; . jest.spyOn(axios,'get').mockResolvedValue(resp);
Copy the code

Ts is error-free and has good mock values. If we write our own service files, we can do this

import * as server from './Server'; . jest.spyOn(server,'getData').mockResolvedValue(resp);
Copy the code

2. Assert in Rxjs

Rxjs is a library for asynchronous operations, with many operators such as map, pipe… We don’t want to use the assertion in the operator, we mock out the result, so we can just assert in the subscribe. However, we need to be careful when we mock Rxjs methods

import {of} from 'rxjs';
import * as server from './Server'; . jest.spyOn(server,'getData').mockResolvedValue(of(resp));// We need to use "of" to send mock data
Copy the code

Also remember to write done() at the end of the feed.

3. Influenced by withRouter

The react-router-dom component reads routing parameters in a number of ways, the best being the high-order withRouter component. However, this can cause a big problem if we use the enzyme to mount the component in the usual way

import App from './App';// The App component uses withRouter. const wapper = mount(<App/>);
Copy the code

However, the command line reminds us

This is when we need to wrap the Router component around it

import App from './App';// The App component uses withRouter
import {MemoryRouter} from 'react-router-dom'; . const wapper = mount(<MemoryRouter><App/></MemoryRouter>);
Copy the code

At this point it feels like everything is going well. However, if our App component used in componentWillReceiveProps when we find the life cycle, Using enzyme setProps () method can not inject new props to App componentWillReceiveProps life cycle. Because we used setProps to inject parameters into the MemoryRouter. We can then encapsulate a new class to test

import App from './App';// The App component uses withRouter
import {MemoryRouter} from 'react-router-dom';
class testApp extends React.Component<Iprops>{ // The Iprops type is the Props type of the App
	render(){
		return(
			<MemoryRouter><App {. this.props} / ></MemoryRouter>)}}... const wapper = mount(<testApp />);
Copy the code

Here you can look more perfect solution componentWillReceiveProps here preach and problems. How do you get the state in your App component? Or internal props? Let’s revisit the mount () method described on the official website;

This can be followed by an option configuration. Provides context configuration, childContextTypes. So we find the related type of the router childContext as follows:

So we can sort out options a little bit: (thanks to Zhu Delong here)

const option ={
 childContextTypes: {
      router: (a)= > void 0.// The router of the package is a function type
    },
 context: {               // Context-specific configuration
     router: {
     history: createMemoryHistory(),
     route: {
          location: {
            hash: ' '.pathname: ' '.search: ' '.state: ' ',},match: { params: {}, isExact: false.path: ' '.url: ' '},}}}}... const wapper = mount(<App/>,option);
Copy the code

Now you can manipulate the App components. Get the state, change the props, do the file on the enzyme.

Original link: tech.gtxlab.com/jest-proble…


About the author: Zhang Shuan, Renhe future big data front-end engineer, focusing on HTML/CSS/JS learning and development.