{"id":657,"date":"2021-01-14T20:51:33","date_gmt":"2021-01-14T20:51:33","guid":{"rendered":"https:\/\/jmrowe.com\/blog\/?p=657"},"modified":"2021-01-15T21:15:11","modified_gmt":"2021-01-15T21:15:11","slug":"testing-reactjs-components","status":"publish","type":"post","link":"https:\/\/jmrowe.com\/blog\/testing-reactjs-components\/","title":{"rendered":"Mocks: Testing Reactjs Components"},"content":{"rendered":"<p>Have recently been learning how to test Reactjs components via unit testing using jest and @testing-library\/react which come prebundled with any new Reactjs app created with CRA (Create-react-app CLI).<\/p>\n<h3>Mocking functions with jest<\/h3>\n<p>Many times components have eventHandlers (onclick, onchange, onblur etc) passed down to them via props and with tests these event handlers are usually mocked out.<\/p>\n<h3>Mocking ajax\/fetch\/axios calls in tests.<\/h3>\n<p>Since almost every app has a remote request that they make, learning to mock async calls seemed very important. For this, a package called MSW ( model service worker) can be used to intercept ajax calls to external remote sources to keep them local so that tests are less flaky and quicker.<\/p>\n<p>My package.json for the example that I&#8217;m running:<\/p>\n<pre class=\"lang:default decode:true\">{\r\n  \"name\": \"unit-testing-sandbox\",\r\n  \"version\": \"1.0.0\",\r\n  \"description\": \"\",\r\n  \"keywords\": [],\r\n  \"main\": \"src\/index.js\",\r\n  \"dependencies\": {\r\n    \"axios\": \"0.21.1\",\r\n    \"react\": \"17.0.0\",\r\n    \"react-dom\": \"17.0.0\",\r\n    \"react-scripts\": \"3.4.3\"\r\n  },\r\n  \"devDependencies\": {\r\n    \"typescript\": \"3.8.3\",\r\n    \"@testing-library\/jest-dom\": \"5.11.9\",\r\n    \"@testing-library\/react\": \"11.2.3\",\r\n    \"@testing-library\/user-event\": \"12.6.0\",\r\n    \"jest-environment-jsdom-sixteen\": \"^1.0.3\",\r\n    \"msw\": \"0.25.0\",\r\n  },\r\n  \"scripts\": {\r\n    \"start\": \"react-scripts start\",\r\n    \"build\": \"react-scripts build\",\r\n    \"test\": \"react-scripts test --env=jest-environment-jsdom-sixteen\",\r\n    \"eject\": \"react-scripts eject\"\r\n  },\r\n  \"browserslist\": [\r\n    \"&gt;0.2%\",\r\n    \"not dead\",\r\n    \"not ie &lt;= 11\",\r\n    \"not op_mini all\"\r\n  ]\r\n}\r\n<\/pre>\n<p>Notice the inclusion of jest-enviroment-jsdom-sixteen. Further more, it&#8217;s use here:<\/p>\n<pre class=\"lang:default decode:true \">\"test\": \"react-scripts test --env=jest-environment-jsdom-sixteen\"<\/pre>\n<p>I added this to suppress some errors about not using act() during async calls. It can be further explained here: https:\/\/kentcdodds.com\/blog\/fix-the-not-wrapped-in-act-warning<\/p>\n<p>To use MSW we have to setup a server with endpoints that it will intercept. Might want to break your endpoints in to a separate file but for this example I kept them all in the same server.js under the mock directory.<\/p>\n<pre class=\"lang:default decode:true\">\/\/ src\/mocks\/server.js\r\nimport { rest,setupWorker } from \"msw\";\r\nimport { setupServer } from \"msw\/node\";\r\n\r\nconst handlers = [\r\n  rest.get(\"https:\/\/jsonplaceholder.typicode.com\/todos\/1\", (req, res, ctx) =&gt; {\r\n\r\n    return  res(ctx.json({ title: \"the mocked title\" }));\r\n  })\r\n];\r\n\r\nconst server = setupServer(...handlers);\r\n\r\nexport { server, rest };\r\n<\/pre>\n<p>What above will do is intercept any call to &#8220;https:\/\/jsonplaceholder.typicode.com\/todos\/1&#8221;\u00a0 and instead return a json object of<\/p>\n<pre class=\"lang:default decode:true \">{title:\"the mocked title\"}<\/pre>\n<p>Also, we want our server to setup and tear down for each and every test and for that we create a setupTests.js within the \/src folder:<\/p>\n<pre class=\"lang:default decode:true \">import \"@testing-library\/jest-dom\/extend-expect\";\r\nimport { server } from \".\/mocks\/server\";\r\n\r\n\/\/ Establish API mocking before all tests.\r\nbeforeAll(() =&gt; server.listen());\r\n\r\n\/\/ Reset any request handlers that we may add during the tests,\r\n\/\/ so they don't affect other tests.\r\nafterEach(() =&gt; server.resetHandlers());\r\n\r\n\/\/ Clean up after the tests are finished.\r\nafterAll(() =&gt; server.close());\r\n<\/pre>\n<p>The testing library should automatically see this setupTests.js file and run it during testing.<\/p>\n<p>So the actual component we are going to test is called Todo.js<\/p>\n<pre class=\"lang:default decode:true\">import React from \"react\";\r\nimport axios from \"axios\";\r\n\r\nexport default function Todo({ownersName}) {\r\n    const [counter, setCounter] = React.useState(0);\r\n    const [title, setTitle] = React.useState(false);\r\n    axios.defaults.withCredentials = true;\r\n    React.useEffect(() =&gt; {\r\n\r\n        async function fetchData() {\r\n            axios.get(\"https:\/\/jsonplaceholder.typicode.com\/todos\/1\").then((res) =&gt; {\r\n                setTitle(res.data.title);\r\n            }).catch(e =&gt; {\r\n                console.log(e.message)\r\n            });\r\n        }\r\n\r\n        fetchData();\r\n\r\n    }, []);\r\n\r\n    return (\r\n        &lt;div&gt;\r\n            {\/**\r\n             Title is retrieved on load via async call to a rest api.\r\n             the h2(with the title) will only render once a value for title is set\r\n             *\/}\r\n            {title &amp;&amp; &lt;h2 data-testid=\"title\"&gt;{title}&lt;\/h2&gt;}\r\n            &lt;p data-testid={\"ownersName\"}&gt;{ownersName}&lt;\/p&gt;\r\n            &lt;p data-testid={\"counter\"}&gt;{counter}&lt;\/p&gt;\r\n\r\n            &lt;button\r\n                data-testid=\"increaseTodo\"\r\n                onClick={() =&gt; {\r\n                    setCounter(counter + 1);\r\n                }}\r\n            &gt;\r\n                Increase this Todo's counter by 1\r\n            &lt;\/button&gt;\r\n        &lt;\/div&gt;\r\n    );\r\n}\r\n<\/pre>\n<p>The ajax call is actually within the useEffect that gets called once on the initial rendering of the component.<\/p>\n<p>Here is our testing file for Todo component. Todo.test.js<\/p>\n<pre class=\"lang:default decode:true\">import React from \"react\";\r\nimport { render, screen, fireEvent, waitFor } from \"@testing-library\/react\";\r\nimport Todo from \".\/Todo\";\r\n\r\nit(\"msw intercepts the axios on effect \", async () =&gt; {\r\n\r\n  render(&lt;Todo ownersName={\"jason\"} \/&gt;);\r\n  await waitFor(()=&gt;expect(screen.getByTestId(\"title\").textContent).toBe(\"the mocked title\"));\r\n\r\n});<\/pre>\n<p>The actual test we are interested in is &#8220;msw intercepts the axios on effect&#8221; as this is the one that will check if the ajax call was intercept and replaced with our mocked return\u00a0 data that we get from MSW.<\/p>\n<p>First we render the component which will trigger the intitial useEffect call which then triggers the ajax call to ultimately update our title state.<\/p>\n<p>The line below enables us to &#8220;wait&#8221;(async) for the ajax call to complete and for the state to be updated before we check to see if the mocked data was in fact returned via MSW.<\/p>\n<pre class=\"lang:default decode:true \">await waitFor(()=&gt;expect( screen.getByTestId(\"title\").textContent).toBe(\"the mocked title\"));<\/pre>\n<p>Our tests pass as MSW does infact return the mocked title of &#8220;the mocked title&#8221; which is what our test is looking for.<\/p>\n<h3>Mocking 3rd party libraries or modules<\/h3>\n<p>We can use jest to mock a library so our tests are easier to write without going in to the complexity of incorporating the libraries that maybe used. For example:<\/p>\n<pre class=\"lang:default decode:true\">\/\/SomeComponent.test.js\r\n\r\nimport React from 'react';\r\nimport { render, screen, act } from '@testing-library\/react';\r\nimport { useGetLocation } from 'react-get-location';\r\n\r\njest.mock('react-get-location\"); \r\n\/\/ this will make \r\n\/\/ jest return a mocked function for every exported function in this module.\r\n\r\ntest('it shows current location', () =&gt; {\r\n\r\n  let setReturnValue;\r\n  function useMockGetLocation() {\r\n    const state = React.useState([]);\r\n    setReturnValue = state[1];\r\n    return state[0];\r\n  }\r\n  useGetLocation.mockImplemention(useMockGetLocation);\r\n\r\n});<\/pre>\n<p>We mocked the useGetLocation function that is from the &#8216;react-get-location&#8217; library and made it so we return a custom state hook to be used in our tests instead of the normal non-mocked version of useGetLocation<\/p>\n<p>(video lesson 184 )<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Have recently been learning how to test Reactjs components via unit testing using jest and @testing-library\/react which come prebundled with any new Reactjs app created with CRA (Create-react-app CLI). Mocking functions with jest Many times components have eventHandlers (onclick, onchange, onblur etc) passed down to them via props and with tests these event handlers are [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[18],"tags":[],"class_list":["post-657","post","type-post","status-publish","format-standard","hentry","category-testing"],"_links":{"self":[{"href":"https:\/\/jmrowe.com\/blog\/wp-json\/wp\/v2\/posts\/657","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jmrowe.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jmrowe.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jmrowe.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jmrowe.com\/blog\/wp-json\/wp\/v2\/comments?post=657"}],"version-history":[{"count":9,"href":"https:\/\/jmrowe.com\/blog\/wp-json\/wp\/v2\/posts\/657\/revisions"}],"predecessor-version":[{"id":677,"href":"https:\/\/jmrowe.com\/blog\/wp-json\/wp\/v2\/posts\/657\/revisions\/677"}],"wp:attachment":[{"href":"https:\/\/jmrowe.com\/blog\/wp-json\/wp\/v2\/media?parent=657"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jmrowe.com\/blog\/wp-json\/wp\/v2\/categories?post=657"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jmrowe.com\/blog\/wp-json\/wp\/v2\/tags?post=657"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}