Typeerror: Cannot Read Property 'stoppropagation' of Undefined Enzyme
React continues to exist the web framework of choice for many UI developers, second merely to jQuery, according to Stack Overflow. Information technology provides an intuitive model for building data-driven user interfaces, and efficiently updates the DOM when this data changes. React pairs nicely with Redux, which enables managing the data that React needs to render interfaces. Redux offers a predictable way to structure and update the data in those frontend applications.
In this tutorial, we will explore how to write tests for an existing React and Redux awarding. Nosotros will then configure continuous integration with CircleCI to automate the testing and ensure that whatever new code we add does not break the existing functionality.
Prerequisites
To follow along with the tutorial, a few things are required:
- A basic familiarity with React and Redux
- The Jest test runner installed on your machine
- The Enzyme testing utility installed on your machine
- A CircleCI account
- A GitHub business relationship
Getting started
For this project, we will use the Reddit API case app in the Redux docs equally our example app. We volition use this application to bear witness how you would add tests to a real app, which should assist when yous are building your ain applications.
Before calculation the tests, we demand to find out what the app does. The best manner to do this is to clone the app and run it locally. To clone the application and install its dependencies run:
git clone https://github.com/reactjs/redux.git cd redux/examples/async git checkout db5f8d1 npm install npm start Note: The git checkout command checks out the redux repo to the latest commit at the fourth dimension of this writing. By the time you lot read this, at that place might be some more changes made to the repo, so running this command ensures that we all accept the same starting point for the tutorial.
Walkthrough of the awarding's functionality
The sample app was designed to brandish the current headlines in a selected subreddit, past fetching the data from the Reddit API. Users can select the subreddit they want to see headlines for, and they are loaded and displayed on the screen. Users can also update the data displayed for the currently selected subreddit, past clicking a Refresh button.
This simple app is a skillful example because information technology has all the components we need for most real-world apps, including:
- Fetching information from an API
- User interactions
- Synchronous and asynchronous actions
- Presentational and container components
Keeping track of our progress with Git and GitHub
As nosotros work on our app, we volition need to rails changes with Git. The first step is to create a new repository, which volition aid u.s.a. keep track of our changes separately from the redux repository. Create a new git repo in the async binder by running:
git init This new repository will rails changes but in the async folder, ignoring the residuum of the code we pulled when nosotros cloned the redux repository. This is a good fourth dimension to brand an initial commit in our new git repository mark the point where we imported the code from redux:
git add . git commit -m "Import async example from redux" We will also need to create a new repository on GitHub where we will push our code. This will come in handy when we volition demand to integrate with CircleCI later on. Get alee and create a new GitHub repo and push button the code we have to the repo.
This tutorial is an excellent resources if you are not familiar with GitHub.
By at present you should accept an understanding of what our case app does and you lot should have a copy of it on your ain GitHub account. Our adjacent stride is adding the tests.
Testing React components
Test setup
Now nosotros are going to need to use the testing tools mentioned before: Jest and Enzyme.
If yous check the packet.json file, you will notice we already have a test command configured. Start with removing the extra flags given in that control, for example: --env=node --passWithNoTests. We will non need those for the remainder of the tutorial.
"test": "react-scripts exam" react-scripts comes with jest installed and configured, so we do not need to install information technology again. We do demand to install enzyme though, and its adapter for our version of React:
npm install --save-dev enzyme enzyme-adapter-react-xvi Adjacent, we need to configure enzyme to use the adapter. react-scripts supports configuring testing tools in a src/setupTests.js file.
Create that file and enter:
import Enzyme from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; Enzyme.configure({ adapter: new Adapter() }); We volition employ snapshot testing to rails changes to components. Using this technique, we take snapshots of our components. When the component's rendered output changes, we can easily detect the changes fabricated. The snapshots are also readable, and so it is an easier mode of verifying that components return the expected output.
To utilize the snapshot technique, nosotros need to install the enzyme-to-json parcel to convert our React components to a snapshot during testing:
npm install --relieve-dev enzyme-to-json We likewise need to configure jest to use this package as the snapshot serializer. Configure this in package.json by adding:
"jest": { "snapshotSerializers": [ "enzyme-to-json/serializer" ] } Now we are set to begin the actual testing.
Component tests
We will begin by writing tests for the App component. A skilful starting point is adding a snapshot test to ensure that the component renders the expected output, given the required props.
Get-go, we need to import the App component, but the only export in App.js is the redux-connected version of the component: export default connect(mapStateToProps)(App). We want to test the rendering of the component and not its interaction with redux, so we will need to as well consign the underlying App component. Do this by adding this snippet to App.js:
export { App }; To epitomize, this is how the App.js file should look right at present:
import React, { Component } from 'react' import PropTypes from 'prop-types' import { connect } from 'react-redux' import { selectSubreddit, fetchPostsIfNeeded, invalidateSubreddit } from '../actions' import Picker from '../components/Picker' import Posts from '../components/Posts' class App extends Component { static propTypes = { selectedSubreddit: PropTypes.string.isRequired, posts: PropTypes.array.isRequired, isFetching: PropTypes.bool.isRequired, lastUpdated: PropTypes.number, dispatch: PropTypes.func.isRequired } componentDidMount() { const { dispatch, selectedSubreddit } = this.props acceleration(fetchPostsIfNeeded(selectedSubreddit)) } componentWillReceiveProps(nextProps) { if (nextProps.selectedSubreddit !== this.props.selectedSubreddit) { const { dispatch, selectedSubreddit } = nextProps dispatch(fetchPostsIfNeeded(selectedSubreddit)) } } handleChange = nextSubreddit => { this.props.acceleration(selectSubreddit(nextSubreddit)) } handleRefreshClick = e => { e.preventDefault() const { dispatch, selectedSubreddit } = this.props dispatch(invalidateSubreddit(selectedSubreddit)) dispatch(fetchPostsIfNeeded(selectedSubreddit)) } render() { const { selectedSubreddit, posts, isFetching, lastUpdated } = this.props const isEmpty = posts.length === 0 return ( <div> <Picker value={selectedSubreddit} onChange={this.handleChange} options={[ 'reactjs', 'frontend' ]} /> <p> {lastUpdated && <bridge> Last updated at {new Appointment(lastUpdated).toLocaleTimeString()}. {' '} </bridge> } {!isFetching && <push button onClick={this.handleRefreshClick}> Refresh </button> } </p> {isEmpty ? (isFetching ? <h2>Loading...</h2> : <h2>Empty.</h2>) : <div style={{ opacity: isFetching ? 0.v : 1 }}> <Posts posts={posts} /> </div> } </div> ) } } const mapStateToProps = country => { const { selectedSubreddit, postsBySubreddit } = state const { isFetching, lastUpdated, items: posts } = postsBySubreddit[selectedSubreddit] || { isFetching: true, items: [] } render { selectedSubreddit, posts, isFetching, lastUpdated } } consign default connect(mapStateToProps)(App) export { App }; Past convention, jest will find test files with names ending in .test.js under any folder named __tests__. That means we need to create the containers binder, and so create the __tests__ directory and nether information technology, create an App.test.js file.
App is exported now, then we can now import it by adding to our test file:
import { App } from '../App' Because nosotros are testing the App component independently of redux, anything that is provided by redux (for example, the component'due south props), will need to be provided explicitly. We can add together some rendering tests to encounter how this works in practise. Inside App.examination.js, add our kickoff examination:
import React from 'react' import { shallow } from 'enzyme' import toJson from 'enzyme-to-json' import { App } from '../App' describe('App', () => { it('renders without crashing given the required props', () => { const props = { isFetching: imitation, dispatch: jest.fn(), selectedSubreddit: 'reactjs', posts: [] } const wrapper = shallow(<App {...props} />) expect(toJson(wrapper)).toMatchSnapshot() }) }) In this exam, we want to verify that the App renders given all the required props. By providing the props we are simulating what redux will do for us in the actual app. jest provides a mock function that we tin use in identify of the real part in our tests. For this case, we employ this to mock the acceleration part. This part will exist chosen in place of the actual dispatch function in our tests.
Y'all can run the tests with the npm test command. Notice the jest test runner kick in, run the tests, and print out a test run summary. You should besides run into this bulletin:
1 snapshot written from 1 test suite. If you open upward src/containers/__tests__/__snapshots__/App.exam.js.snap you should find a snapshot version of the component that shows the component's render output.
Become ahead and add together a couple more tests to exam the rendering behavior of App. Showtime, we will add a test to ensure that the selectedSubreddit prop is ever passed to the Picker component. Nosotros will add this exam just beneath our existing tests:
// Add this import import Picker from '../../components/Picker' it('sets the selectedSubreddit prop every bit the `value` prop on the Picker component', () => { const props = { isFetching: false, dispatch: jest.fn(), selectedSubreddit: 'reactjs', posts: [] } const wrapper = shallow(<App {...props} />) // Query for the Picker component in the rendered output const PickerComponent = wrapper.observe(Picker) await(PickerComponent.props().value).toBe(props.selectedSubreddit) }) This test shows how we tin easily use enzyme to query nested components, (in this instance,Picker), and assert that it is rendered with the correct props. I highly recommend earthworks into enzyme'due south docs to see the range of testing utilities it provides.
Next, nosotros volition add another test to bank check for elements that are rendered based on some status. In this example, we will verify that the Refresh push is rendered when the isFetching prop is false:
it('renders the Refresh push when the isFetching prop is false', () => { const props = { isFetching: false, dispatch: jest.fn(), selectedSubreddit: 'reactjs', posts: [] } const wrapper = shallow(<App {...props} />) await(wrapper.find('button').length).toBe(i) }) Finally, we should add a test that deals with some user interaction. This test can verify that when the Refresh button is clicked, it dispatches the right actions:
// Add this import import * as actions from '../../actions'; it('handleRefreshClick dispatches the correct deportment', () => { const props = { isFetching: false, acceleration: jest.fn(), selectedSubreddit: 'reactjs', posts: [] } // Mock outcome to be passed to the handleRefreshClick role const mockEvent = { preventDefault: jest.fn() } // Mock the actions we expect to be chosen deportment.invalidateSubreddit = jest.fn(); deportment.fetchPostsIfNeeded = jest.fn(); const wrapper = shallow(<App {...props} />) // Call the function on the component case, passing the mock event wrapper.example().handleRefreshClick(mockEvent); expect(mockEvent.preventDefault).toHaveBeenCalled(); expect(props.acceleration.mock.calls.length).toBe(3); look(actions.invalidateSubreddit.mock.calls.length).toBe(i); expect(actions.fetchPostsIfNeeded.mock.calls.length).toBe(2); }) We need to start off by importing actions, considering we will mock some of the functions it provides. In the exam, we provide the usual props and and so a mockEvent object. We will utilize the mockEvent object to simulate a click event sent by the browser when the button is clicked. The mocked result needs to contain a preventDefault property, which should be a office as it will exist chosen within the handleRefreshClick function. Without it, nosotros would get an fault informing us of the missing belongings: e.preventDefault is non a office.
In one case we return the component using shallow, nosotros and so manually phone call handleRefreshClick, passing in the mock event to simulate what will happen when the function is called in our app. We assert the following properties of our app:
-
result.preventDefaultshould take been called once -
props.dispatchshould take been called 3 times- Once on
componentDidMountsince lifecycle hooks are executed by the shallow rendering API - Twice in the
handleRefreshClickpart
- Once on
-
actions.invalidateSubredditshould have been called one time -
actions.fetchPostsIfNeededshould have been called twice- The first call happens in
componentDidMount - The second phone call happens within
handleRefreshClick
- The first call happens in
To make certain our expectations of the componentDidMount function calls are correct, nosotros tin can include these assertions right before the handleRefreshClick function call.
const wrapper = shallow(<App {...props} />) // The next assertions are for functions called in componentDidMount expect(props.acceleration.mock.calls.length).toBe(ane); expect(actions.fetchPostsIfNeeded.mock.calls.length).toBe(1); wrapper.case().handleRefreshClick(mockEvent); //... remainder of test omitted for brevity At this bespeak, nosotros have tested the about challenging parts of the code, and that should give united states of america a good starting indicate to comfortably add together tests for any other component functionality.
Testing Redux functionality
In this section, we will add some tests for the redux related parts of our awarding, specifically the actions and the reducers.
Testing action creators
Nosotros will start off with the activeness creators. This app, has both asynchronous and synchronous action creators. Asynchronous action creators are used in conjunction with redux-thunk to enable async operations that do non immediately produce a event, similar fetching information. Synchronous activity creators return manifestly objects. Nosotros will embrace how to test both.
For reference, this is how our src/actions/alphabetize.js file looks:
export const REQUEST_POSTS = 'REQUEST_POSTS' export const RECEIVE_POSTS = 'RECEIVE_POSTS' export const SELECT_SUBREDDIT = 'SELECT_SUBREDDIT' export const INVALIDATE_SUBREDDIT = 'INVALIDATE_SUBREDDIT' export const selectSubreddit = subreddit => ({ type: SELECT_SUBREDDIT, subreddit }) export const invalidateSubreddit = subreddit => ({ blazon: INVALIDATE_SUBREDDIT, subreddit }) export const requestPosts = subreddit => ({ type: REQUEST_POSTS, subreddit }) export const transformResponseBody = (json) => { return json.data.children.map(child => kid.data); } export const receivePosts = (subreddit, json) => ({ type: RECEIVE_POSTS, subreddit, posts: transformResponseBody(json), receivedAt: Appointment.at present() }) const fetchPosts = subreddit => dispatch => { acceleration(requestPosts(subreddit)) return fetch(`https://www.reddit.com/r/${subreddit}.json`) .then(response => response.json()) .and then(json => dispatch(receivePosts(subreddit, json))) } const shouldFetchPosts = (state, subreddit) => { const posts = state.postsBySubreddit[subreddit] if (!posts) { return true } if (posts.isFetching) { render false } render posts.didInvalidate } export const fetchPostsIfNeeded = subreddit => (dispatch, getState) => { if (shouldFetchPosts(getState(), subreddit)) { render dispatch(fetchPosts(subreddit)) } } Our next task is to create the files that enable testing. Inside src/deportment/, create a folder named __tests__ and inside that, create a file called actions.exam.js.
We volition start with the synchronous action creators, which are simply pure functions which accept some data and return an activeness object. We should check that given the necessary arguments, the activeness creator returns the right action. Nosotros tin demonstrate this with a examination for the selectSubreddit action creator, which accepts a subreddit every bit an argument then returns an action.
import * as actions from '../index' describe('actions', () => { const subreddit = 'reactjs' describe('selectSubreddit', () => { information technology('should create an activity with a given subreddit', () => { const expectedAction = { type: actions.SELECT_SUBREDDIT, subreddit } await(actions.selectSubreddit(subreddit)).toEqual(expectedAction) }) }) }) For most synchronous action creators, that is all we need to do.
To make our piece of work easier once we continue to test the async activeness creators, nosotros can add together a test for the receivePosts action creator too. This is how the function looks:
consign const receivePosts = (subreddit, json) => ({ type: RECEIVE_POSTS, subreddit, posts: json.data.children.map(child => child.data), receivedAt: Date.at present() }) In the returned action, we have a transformation happening in the posts belongings. Excerpt this into a new function telephone call that takes the json argument and does the transformation we need. Our new version of the receivePosts office will be as follows. Also, accept note that we have to export the new helper function so that we can access it in the tests later on.
export const transformResponseBody = (json) => { return json.data.children.map(child => child.data); } export const receivePosts = (subreddit, json) => ({ blazon: RECEIVE_POSTS, subreddit, posts: transformResponseBody(json), receivedAt: Date.now() }) Yous may detect that in the returned activity, there is a receivedAt holding, which returns Date.at present(). In our examination, we will skip testing this holding since it changes each time the function is called. Yous can examination this on your own past mocking the Date.now part, simply for the purposes of this tutorial, nosotros will skip this footstep.
Now that nosotros accept selected the scope of what we demand to do, nosotros need to add the examination for the receivePosts action creator:
draw('actions', () => { const subreddit = 'reactjs' // Add the mockJSON response const mockJSON = { data: { children: [{ data: { title: "Postal service 1" } }, { data: { title: "Postal service 2" } }] } }; // ... other tests... draw('receivePosts', () => { it('should create the expected action', () => { const expectedAction = { blazon: actions.RECEIVE_POSTS, subreddit, posts: actions.transformResponseBody(mockJSON), } expect(actions.receivePosts(subreddit, mockJSON)).toMatchObject(expectedAction); }) }) }) Note that we are using toMatchObject to match just a subset of the returned activity object, which excludes matching the receivedAt key.
Tests for the rest of the synchronous action creators follow the same process where given some data, we test that the correct action is returned.
Time to exam async action creators, and specifically, the fetchPosts activeness creator. The starting time thing we need to do is to consign the function, and we will do this by adding export to the function so information technology becomes:
export const fetchPosts = subreddit => dispatch => { dispatch(requestPosts(subreddit)) return fetch(`https://world wide web.reddit.com/r/${subreddit}.json`) .so(response => response.json()) .and then(json => dispatch(receivePosts(subreddit, json))) } We also demand to install a few new packages:
npm install --relieve-dev fetch-mock redux-mock-store Nosotros will use fetch-mock to mock HTTP requests fabricated using fetch and redux-mock-store to help us create a mock store to utilise in the tests. Add the tests equally follows:
// Add the new imports import thunk from 'redux-thunk' import fetchMock from 'fetch-mock' import configureMockStore from 'redux-mock-store' const middlewares = [thunk] const mockStore = configureMockStore(middlewares) draw('actions', () => { const subreddit = 'reactjs' const mockJSON = { information: { children: [{ data: { title: "Post ane" } }, { data: { title: "Postal service ii" } }] } }; // ... other tests... draw("fetchPosts", () => { afterEach(() => { // restore fetch() to its native implementation fetchMock.restore() }) it("creates REQUEST_POSTS and RECEIVE_POSTS when fetching posts", () => { // Mock the returned data when we call the Reddit API fetchMock.getOnce(`https://www.reddit.com/r/${subreddit}.json`, { trunk: mockJSON }) // The sequence of actions nosotros expect to be dispatched const expectedActions = [ { type: deportment.REQUEST_POSTS }, { blazon: actions.RECEIVE_POSTS, subreddit, posts: actions.transformResponseBody(mockJSON) } ] // Create a store with the provided object as the initial state const store = mockStore({}) return store.acceleration(actions.fetchPostsIfNeeded(subreddit)).then(() => { expect(store.getActions()).toMatchObject(expectedActions) }) }) }) }) We start with all the necessary imports, including redux-thunk. For this case, we need to configure an actual shop, and this means that we will likewise apply the middleware to the mock shop as well.
Moving on, we have an afterEach part which runs after each test and ensures that we restore the original fetch implementation. That is so our mock implementation is not used in other tests.
Next nosotros mock the request we expect to be made and provide a mock trunk that will exist returned every bit the response body. We then ascertain the sequence of actions we expect to be taken when we phone call fetchPosts. This sequence implies that when fetchPosts is dispatched, it should generate a REQUEST_POSTS activeness, then RECEIVE_POSTS with the posts for the requested subreddit. We will besides exclude the receivedAt property in the RECEIVE_POSTS activity, as in the previous examination, and add the transformed response body in the posts key, as we did before.
Next, we create the store, giving information technology some initial state then acceleration the fetchPosts. Finally, we affirm that the list of actions applied to the store should match the sequence in our expectedActions array.
Re-running our tests at this indicate should ostend that everything passes.
This concludes our action creators testing. Next we will review how to test reducers.
Testing reducers The reducers are at the centre of redux because they are how we update the state of our whole application. The reducer tests should help u.s. verify that each of our dispatched actions updates the state as expected.
Here are the contents of the reducers/alphabetize.js file that we are going to test:
import { combineReducers } from 'redux' import { SELECT_SUBREDDIT, INVALIDATE_SUBREDDIT, REQUEST_POSTS, RECEIVE_POSTS } from '../actions' const selectedSubreddit = (state = 'reactjs', activeness) => { switch (action.type) { case SELECT_SUBREDDIT: return action.subreddit default: return state } } const posts = (state = { isFetching: false, didInvalidate: imitation, items: [] }, action) => { switch (activeness.blazon) { example INVALIDATE_SUBREDDIT: return { ...state, didInvalidate: true } example REQUEST_POSTS: return { ...land, isFetching: true, didInvalidate: imitation } case RECEIVE_POSTS: return { ...country, isFetching: false, didInvalidate: faux, items: activity.posts, lastUpdated: activeness.receivedAt } default: render state } } const postsBySubreddit = (state = { }, action) => { switch (activeness.type) { case INVALIDATE_SUBREDDIT: case RECEIVE_POSTS: case REQUEST_POSTS: return { ...land, [action.subreddit]: posts(land[action.subreddit], action) } default: render state } } const rootReducer = combineReducers({ postsBySubreddit, selectedSubreddit }) consign default rootReducer In our reducer file, we take ii reducers, each of which manages its own office of the state. Eventually they volition be merged into a unmarried root reducer using combineReducers. Nosotros are going to export the individual reducer functions to make testing more convenient by adding this snippet to reducers/index.js
export { postsBySubreddit, selectedSubreddit } Create a __tests__ directory nether reducers and so create a reducers.test.js file inside that directory, which is where our tests will get. Because it is the simpler of the two, nosotros will test the selectedSubreddit reducer commencement.
import { SELECT_SUBREDDIT, INVALIDATE_SUBREDDIT, REQUEST_POSTS, RECEIVE_POSTS } from '../../deportment' import { postsBySubreddit, selectedSubreddit } from '../index' describe('app reducer', () => { depict('selectedSubreddit', () => { information technology('should return the default state', () => { await(selectedSubreddit(undefined, {})).toBe('reactjs') }) it('should update the selectedSubreddit', () => { const subreddit = 'frontend' const action = { blazon: SELECT_SUBREDDIT, subreddit } expect(selectedSubreddit(undefined, action)).toBe(subreddit) }) }) }) Our kickoff test checks that the selectedSubreddit reducer correctly initialises the land. When given an undefined country, or an empty action, it should return the default value, which is prepare to reactjs. The side by side check verifies that when the reducer receives a valid activeness object, it correctly updates the country.
At present we can move on to the postsBySubreddit reducer.
describe('postsBySubreddit', () => { const subreddit = 'frontend' it('should return the default state', () => { expect(postsBySubreddit(undefined, {})).toEqual({}) }) it('should handle INVALIDATE_SUBREDDIT', () => { const action = { type: INVALIDATE_SUBREDDIT, subreddit } expect(postsBySubreddit({}, action)).toEqual({ [subreddit]: { isFetching: false, didInvalidate: true, items: [] } }) }) it('should handle REQUEST_POSTS', () => { const activeness = { type: REQUEST_POSTS, subreddit } expect(postsBySubreddit({}, activeness)).toEqual({ [subreddit]: { isFetching: true, didInvalidate: fake, items: [] } }) }) it('should handle RECEIVE_POSTS', () => { const posts = ['post one', 'post 2'] const receivedAt = Date.now() const action = { type: RECEIVE_POSTS, subreddit, posts, receivedAt } expect(postsBySubreddit({}, activeness)).toEqual({ [subreddit]: { isFetching: faux, didInvalidate: fake, items: posts, lastUpdated: receivedAt } }) }) }) Start by testing that it initializes the state correctly. In this case, the default state is an empty object, every bit shown in the first test.
The tests for the remainder of the actions are like; nosotros verify that given an action, the reducer returns the expected country update. The subreddit should be prepare as the key of the returned object and the nested object should be updated according to the rules we have in the reducer.
Yous will notice that the common theme with reducers is that given a item set of inputs (initial state and an action), a new country should be returned. We do all of our assertions on the returned state to ensure information technology is what we expect.
With that section of the tutorial complete, we have covered many parts of a React and redux application that you would need to exam in a typical app.
Continuous integration with GitHub and CircleCI
This is the role of the tutorial where nosotros add continuous integration with CircleCI. Continuous integration helps u.s.a. ensure that any changes nosotros make to the code do not break whatever existing functionality. Our tests volition be run any time we button new lawmaking, whether by adding new commits to an existing branch or past opening a pull request to merge a new branch into the main branch. This helps in communicable bugs early in the development process.
CircleCI configuration
The kickoff thing we demand to add is a configuration file that will tell CircleCI how to test our awarding. The config file needs to exist in a .circleci directory in our root folder and should exist named config.yml.
For our awarding, here is the config file nosotros'll use:
version: 2 jobs: build: working_directory: ~/redux-async docker: - image: circleci/node:viii steps: - checkout - restore_cache: primal: npm-cache-v1-{{ checksum "package-lock.json" }} - run: name: Install Dependencies command: npm ci - save_cache: key: npm-cache-v1-{{ checksum "parcel-lock.json" }} paths: - /domicile/circleci/.npm - run: name: Run Tests command: npm test I would like to have a moment to explore some of the concepts covered here:
- The docker epitome specifies the base docker container nosotros are going to utilize. In our instance, it will be a container pre-installed with Node.js version eight. All the commands that follow will be run in an case of this container image.
- The checkout step checks out the source code to the working directory.
- CircleCI supports caching dependencies then we are taking advantage of this to cache our
npmdependencies. - The
restore_cachepace restores any cache that is available from a previous build. - In the
runpace, we usenpm cito install our projection dependencies. - The
save_cachestep is where we salvage our cache of thenpmdependencies. The/home/circleci/.npmfolder is where thenpmenshroud is stored when we usenpm cito install the dependencies. - We create a cache that uses a checksum of the contents of the
bundle-lock.jsonfile. If this file changes, a new cache volition be created. - Information technology is also worth noting that caches are immutable in CircleCI . That ways that in one case a cache is created, there is no changing it later on. To change a enshroud, you need to create a new one entirely. The
v1office of our cache key helps usa to invalidate the cache. In this case, if nosotros needed to manually force the cache to exist re-created, nosotros could change that tov2. - The concluding command is the bodily test command that runs our tests.
To get a much ameliorate overview of the CircleCI config format and all the options available for a JavaScript project, become to https://circleci.com/docs/2.0/language-javascript/.
Integrating CircleCI and GitHub
Take a moment to make certain y'all have pushed all your changes to the GitHub repository nosotros created before. We will now prepare up CircleCI for to test our lawmaking whenever we make any new changes.
Here is how to add the projection to CircleCI:
- Create a new account on CircleCI if you have not already created one.
-
In one case you are logged in, ensure your account is selected on the top left corner.
- Click Add Projects.
-
On the adjacent screen, search for the proper name of your GitHub repository, then click Fix Project.
- On the next folio, gyre downward to Next Steps and click Start Building.
CircleCI will now run our examination steps. In a short while, you should get a successful build. 🎉
With our CI process set up, any new commit that is pushed in the repository volition trigger a exam run to make sure none of our changes volition interruption the build. In cases where changes we made cause the tests to fail, nosotros will be notified, and nosotros can track exactly which commit acquired the failure.
Conclusion
This concludes our exploration of calculation tests to a real-globe React and Redux application. It is my hope that the concepts covered hither will be helpful and will set you up for success when working on similar applications in the future.
The post-obit resources were very helpful and will definitely assist out in providing more information on testing React and redux:
-
https://medium.freecodecamp.org/the-right-way-to-test-react-components-548a4736ab22
-
https://redux.js.org/recipes/writingtests
Kevin Ndung'u is a software developer and open source enthusiast currently working as a software engineer at Andela . He is passionate about sharing his knowledge through web log posts and open source code. When not building web applications, yous can find him watching a game of soccer.
hudsonthenecolasty1936.blogspot.com
Source: https://circleci.com/blog/continuously-testing-react-applications-with-jest-and-enzyme/
0 Response to "Typeerror: Cannot Read Property 'stoppropagation' of Undefined Enzyme"
Post a Comment