{"componentChunkName":"component---src-components-posts-page-layout-js","path":"/comparing-enzyme-and-react-testing-library","result":{"data":{"mdx":{"id":"6559a392-7920-5d04-af96-e6cfd8a834d5","body":"function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"title\": \"Comparing Enzyme with React Testing Library\",\n  \"slug\": \"comparing-enzyme-and-react-testing-library\",\n  \"date\": \"2020-09-21\",\n  \"author\": \"Adam Goth\",\n  \"preview\": \"Enzyme has long been a popular library for testing React applications. More recently, React Testing Library has been gaining popularity in Enzyme's place. In this post, we'll take a look at how the two compare.\",\n  \"categories\": [\"testing\"],\n  \"keywords\": [\"javascript\", \"react\", \"web development\", \"testing\", \"react testing library\", \"enzyme\"]\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, [\"components\"]);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://enzymejs.github.io/enzyme/\"\n  }), \"Enzyme\"), \" has long been a popular library for\\ntesting React applications. More recently,\\n\", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://testing-library.com/docs/react-testing-library/intro\"\n  }), \"React Testing Library\"), \"\\nhas been gaining traction in Enzyme's place. In this post, we'll take a look at\\nhow the two compare.\"), mdx(\"h3\", null, \"Overview\"), mdx(\"p\", null, \"Enzyme is a JavaScript Testing utility for React that makes it easier to assert,\\nmanipulate, and traverse your React Components\\u2019 output. It was created by AirBnB\\nand released in 2015. When using Enzyme, it is common to render the React\\ncomponent that you are testing and then test the component based on certain\\nprops or state that are passed in, or by calling functions contained within the\\ncomponent.\"), mdx(\"p\", null, \"While Enzyme tests typically focus on components working correctly internally,\\nReact Testing Library is more focused on testing the React application as it is\\nexperienced by the user. Tests tend to be more focused on the state of the DOM\\nafter imitating user behavior rather than the state of a particular component or\\nimplementation.\"), mdx(\"p\", null, \"To get a better understanding of this, let's look at some code.\"), mdx(\"h3\", null, \"Setting up\"), mdx(\"p\", null, \"In order to compare these two testing libraries, I've created two separate\\nrepos. Both projects contain the same exact application (a to-do list, of\\ncourse). The only difference is that one test file is written using Enzyme and\\nthe other is written using React Testing Library. You can easily follow along in\\nthis post without running the application, but if you are interested, both repos\\nare available on GitHub.\"), mdx(\"p\", null, mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://github.com/adamgoth/testing-with-enzyme\"\n  }), \"Repo for testing with Enzyme\")), mdx(\"p\", null, mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://github.com/adamgoth/testing-with-react-testing-library\"\n  }), \"Repo for testing with React Testing Library\")), mdx(\"p\", null, \"The file we're going to focus on in both repos is \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"src/components/ToDo.test.js\"), \".\"), mdx(\"p\", null, \"Below is our testing file, written in the typical style of Enzyme.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-jsx\"\n  }), \"// testing-with-enzyme/src/components/ToDo.test.js\\n\\nimport React from \\\"react\\\"\\nimport { mount } from \\\"enzyme\\\"\\nimport ToDo from \\\"./ToDo\\\"\\n\\nconst setup = () => mount(<ToDo />)\\n\\ndescribe(\\\"<ToDo/>\\\", () => {\\n  describe(\\\"The default UI\\\", () => {\\n    it(\\\"Renders two default todo items\\\", () => {\\n      const app = setup()\\n      expect(app.find(\\\".ToDoItem\\\").length).toBe(2)\\n    })\\n\\n    it(\\\"Has an input field\\\", () => {\\n      const app = setup()\\n      expect(app.find(\\\".ToDoInput\\\").length).toEqual(1)\\n    })\\n\\n    it(\\\"Has an add button\\\", () => {\\n      const app = setup()\\n      expect(app.find(\\\".ToDo-Add\\\").length).toEqual(1)\\n    })\\n  })\\n\\n  describe(\\\"Adding items\\\", () => {\\n    window.alert = jest.fn()\\n    it(\\\"When the add button is pressed, if the input field is empty, prevent item from being added\\\", () => {\\n      const app = setup()\\n      app.find(\\\".ToDo-Add\\\").simulate(\\\"click\\\")\\n      expect(app.find(\\\".ToDoItem\\\").length).toBe(2)\\n    })\\n\\n    it(\\\"When the add button is pressed, if the input field is empty, prevent item from being added\\\", () => {\\n      const app = setup()\\n      app.find(\\\".ToDo-Add\\\").simulate(\\\"click\\\")\\n      expect(window.alert).toHaveBeenCalled()\\n    })\\n\\n    it(\\\"When the add button is pressed, if the input field has text, it creates a new todo item\\\", () => {\\n      const app = setup()\\n      const event = { target: { value: \\\"Create more tests\\\" } }\\n      app.find(\\\"input\\\").simulate(\\\"change\\\", event)\\n      app.find(\\\".ToDo-Add\\\").simulate(\\\"click\\\")\\n      expect(\\n        app\\n          .find(\\\".ToDoItem-Text\\\")\\n          .at(2)\\n          .text()\\n      ).toEqual(\\\"Create more tests\\\")\\n    })\\n  })\\n\\n  describe(\\\"Deleting items\\\", () => {\\n    it(\\\"When the delete button is pressed for the first todo item, it removes the entire item\\\", () => {\\n      const app = setup()\\n      app\\n        .find(\\\".ToDoItem-Delete\\\")\\n        .first()\\n        .simulate(\\\"click\\\")\\n      expect(app.find(\\\".ToDoItem\\\").length).toBe(1)\\n    })\\n  })\\n})\\n\")), mdx(\"p\", null, \"And then, the same tests, written with React Testing Library.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-jsx\"\n  }), \"// testing-with-react-testing-library/src/components/ToDo.test.js\\n\\nimport React from \\\"react\\\"\\nimport { render } from \\\"@testing-library/react\\\"\\nimport userEvent from \\\"@testing-library/user-event\\\"\\nimport ToDo from \\\"./ToDo\\\"\\n\\nconst setup = () => render(<ToDo />)\\n\\ndescribe(\\\"<ToDo/>\\\", () => {\\n  describe(\\\"The default UI\\\", () => {\\n    it(\\\"Renders two default todo items\\\", () => {\\n      const { getAllByRole } = setup()\\n      expect(getAllByRole(\\\"listitem\\\").length).toBe(2)\\n    })\\n\\n    it(\\\"Has an input field\\\", () => {\\n      const { getByRole } = setup()\\n      expect(getByRole(\\\"textbox\\\")).toBeInTheDocument()\\n    })\\n\\n    it(\\\"Has an add button\\\", () => {\\n      const { getByLabelText } = setup()\\n      expect(getByLabelText(\\\"add\\\")).toBeInTheDocument()\\n    })\\n  })\\n\\n  describe(\\\"Adding items\\\", () => {\\n    it(\\\"When the add button is pressed, if the input field is empty, prevent item from being added\\\", () => {\\n      const { getByLabelText } = setup()\\n      window.alert = jest.fn()\\n      userEvent.click(getByLabelText(\\\"add\\\"))\\n      expect(window.alert).toHaveBeenCalled()\\n    })\\n\\n    it(\\\"When the add button is pressed, if the input field has text, it creates a new todo item\\\", async () => {\\n      const { getByRole, getByLabelText, getByText } = setup()\\n      const toDoItem = \\\"fake item\\\"\\n      userEvent.type(getByRole(\\\"textbox\\\"), toDoItem)\\n      userEvent.click(getByLabelText(\\\"add\\\"))\\n      const item = await getByText(toDoItem)\\n      expect(item).toBeInTheDocument()\\n    })\\n  })\\n\\n  describe(\\\"Deleting items\\\", () => {\\n    it(\\\"When the delete button is pressed for the first todo item, it removes the entire item\\\", async () => {\\n      const { getAllByRole, getByLabelText, queryByText } = setup()\\n      // default item\\n      const toDoItem = \\\"clean the house\\\"\\n      userEvent.click(getByLabelText(`delete ${toDoItem}`))\\n      const item = await queryByText(toDoItem)\\n      expect(item).toBeNull()\\n      // should only be 1 item left\\n      expect(getAllByRole(\\\"listitem\\\").length).toBe(1)\\n    })\\n  })\\n})\\n\")), mdx(\"p\", null, \"Both files test for the following:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Renders two default todo items\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Has an input field\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Has an add button\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"When the add button is pressed, if the input field is empty, prevent item from\\nbeing added\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"When the add button is pressed, if the input field has text, it creates a new\\ntodo item\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"When the delete button is pressed for the first todo item, it removes the\\nentire item\")), mdx(\"p\", null, \"Because we are using Enzyme's \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"mount\"), \" function, the components in both tests are\\nrendered similarly, with an instance of the component being created and then\\nattached to the actual DOM. This would not be true if we had used another\\npopular Enzyme function, \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"shallow\"), \" to render our component. This post does not\\nfocus on that difference, but you can read more about the difference\\n\", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://gist.github.com/fokusferit/e4558d384e4e9cab95d04e5f35d4f913\"\n  }), \"here\"), \".\"), mdx(\"p\", null, \"The first significant way in that the tests start to differ is when we go to\\nsearch for a particular element in the DOM to assert its existence or its state.\\nTypically in an Enzyme test, you'll see elements searched for by their\\nclassname, as follows:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-javascript\"\n  }), \"it(\\\"Renders two default todo items\\\", () => {\\n  const app = setup()\\n  expect(app.find(\\\".ToDoItem\\\").length).toBe(2)\\n})\\n\")), mdx(\"p\", null, \"When writing the same test using React Testing Library, you'll notice that we\\ninstead use a method called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"getAllByRole\"), \", and pass it an\\n\", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques\"\n  }), \"ARIA role\"), \"\\nof \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"listitem\"), \".\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-javascript\"\n  }), \"it(\\\"Renders two default todo items\\\", () => {\\n  const { getAllByRole } = setup()\\n  expect(getAllByRole(\\\"listitem\\\").length).toBe(2)\\n})\\n\")), mdx(\"p\", null, \"So why is one better than the other? While classnames are rather arbitrary, ARIA\\nroles are not. ARIA roles provide additional context to elements for\\naccessibility purposes. In the future, as developers, we may go and update our\\nclassname. We may tweak the name, we may change the style, we may entirely\\nchange how we wrote our CSS. If that happens, all of the sudden our test breaks.\\nBut the application has not broken. By querying by an element's role rather than\\nits classname, we can ensure that we are testing the application by looking for\\nelements in the same manner that a user with assistive technology may be looking\\nat the application. We look for elements based on the purpose they convey to our\\nusers.\"), mdx(\"p\", null, \"This concept is discussed in the React Testing Library docs,\\n\", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://testing-library.com/docs/guide-which-query\"\n  }), \"Which query should I use?\"), \",\\nwhich provides recommendations for the order of priority in which you should\\nquery for elements. For example, if we can't find an element by its role, our\\nnext best bet is to look for a label. Why? Well, that's most likely what our\\nusers would do to find a certain part of the application. This highlights\\n\", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://testing-library.com/docs/guiding-principles\"\n  }), \"React Testing Library's guiding principles\"), \".\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"The more your tests resemble the way your software is used, the more\\nconfidence they can give you.\")), mdx(\"p\", null, \"The library is written to provide methods and utilities that encourage you to\\nwrite tests that closely resemble how your web pages are used. It purposely\\ndrives the user towards accessibility and away from testing implementation\\ndetails.\"), mdx(\"p\", null, \"Let's move on to another example and take a look at the difference in how we\\ntest that our application successfully creates a new item in the to-do list.\"), mdx(\"p\", null, \"With Enzyme, it's common to manually create DOM events and then pass them to\\nEnzyme's \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"simulate\"), \" function, telling it to simulate the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"change\"), \" event with\\nthis event data that we've created. Below is an example of this.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-javascript\"\n  }), \"// testing-with-enzyme/src/components/ToDo.test.js\\n\\nit(\\\"When the add button is pressed, if the input field has text, it creates a new todo item\\\", () => {\\n  const app = setup()\\n  const event = { target: { value: \\\"Create more tests\\\" } }\\n  app.find(\\\"input\\\").simulate(\\\"change\\\", event)\\n  app.find(\\\".ToDo-Add\\\").simulate(\\\"click\\\")\\n  expect(\\n    app\\n      .find(\\\".ToDoItem-Text\\\")\\n      .at(2)\\n      .text()\\n  ).toEqual(\\\"Create more tests\\\")\\n})\\n\")), mdx(\"p\", null, \"While this does what we'd expect to, it doesn't test the application in the same\\nmanner the user would use it. There is a lot of API and implementation\\ninformation we need to know in order for the test to work. We need to know what\\nthe event should look like. We need to know which event API to simulate. We need\\nto know the classname of the element we want to click. We need to know the\\nclassname of the new list item to look for is. And lastly, we need to know what\\norder the element should be in so we can compare the text. None of these things\\nare things the user actually knows or cares about. All they know is that when\\nthey type in the box and then click the add button, a new item is added to the\\nlist.\"), mdx(\"p\", null, \"To get away from testing our code implementation and get closer to testing how\\nthe application is actually used, we turn once again to React Testing Library.\\nInstead of creating fake DOM event objects and simulating various change events,\\nwe have the ability to mimic how users would actually interact with the\\napplication using \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"userEvent\"), \"'s, which are provided by the\\n\", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://github.com/testing-library/user-event\"\n  }), \"user-event\"), \" library.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"user-event tries to simulate the real events that would happen in the browser\\nas the user interacts with it. For example userEvent.click(checkbox) would\\nchange the state of the checkbox.\")), mdx(\"p\", null, \"Using this, the same test written in React Testing Library looks as follows:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-javascript\"\n  }), \"// testing-with-react-testing-library/src/components/ToDo.test.js\\n\\nit(\\\"When the add button is pressed, if the input field has text, it creates a new todo item\\\", async () => {\\n  const { getByRole, getByLabelText, getByText } = setup()\\n  const toDoItem = \\\"fake item\\\"\\n  userEvent.type(getByRole(\\\"textbox\\\"), toDoItem)\\n  userEvent.click(getByLabelText(\\\"add\\\"))\\n  const item = await getByText(toDoItem)\\n  expect(item).toBeInTheDocument()\\n})\\n\")), mdx(\"p\", null, \"In contrast to the Enzyme test, to write the React Testing Library test, we\\ndon't need to know much more than what the user would now. We first look for an\\nelement with the role of \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"textbox\"), \", we then simulate the user typing by using\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"userEvent.type\"), \", we simulate the user clicking with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"userEvent.click\"), \" on the\\nelement with the accessibility label of \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"add\"), \". We then assert that the text we\\ntyped in is appearing in the document.\"), mdx(\"p\", null, \"In addition to being a much closer representation of the user's experience with\\nthe application, writing this test this way also makes for a much less brittle\\ntest. We could update classnames or change the number of items in the list and\\nthe test would still pass because the application would still be working. The\\nsame can not be said for the first test written in Enzyme.\"), mdx(\"h3\", null, \"Wrapping up\"), mdx(\"p\", null, \"These examples are shown to attempt to highlight some of the benefits that React\\nTesting Library offers and how it differs from the more traditional testing\\nlibrary of Enzyme. Everything React Testing Library offers always comes back to\\nits guiding principle.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"The more your tests resemble the way your software is used, the more\\nconfidence they can give you.\")), mdx(\"p\", null, \"We've all been there before when a little tiny change to a component causes a\\ntest to break without actually breaking any functionality. React Testing\\nLibrary, used properly, guides us away from writing these types of\\nimplementation tests and towards writing more accessible code and more robust\\ntests that more closely resemble how the application is used.\"), mdx(\"p\", null, \"While this post is intended to serve as a high-level introduction to React\\nTesting Library and it's baked-in philosophy, it only scratches the surface of\\nall the library has to offer. To learn more, visit the project's site at\\n\", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://testing-library.com/\"\n  }), \"testing-library.com\"), \".\"), mdx(\"p\", null, \"If you enjoyed this post or found it useful, please consider\\n\", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.adamgoth.com%2Fcomparing-enzyme-and-react-testing-library\"\n  }), \"sharing it on Twitter\"), \".\"), mdx(\"p\", null, \"If you want to stay updated on new posts,\\n\", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://twitter.com/intent/follow?original_referer=https%3A%2F%2Fpublish.twitter.com%2F%3FbuttonType%3DFollowButton%26query%3Dhttps%253A%252F%252Ftwitter.com%252Finit_adam%26widget%3DButton&ref_src=twsrc%5Etfw&region=follow_link&screen_name=init_adam&tw_p=followbutton\"\n  }), \"follow me on Twitter\"), \".\"), mdx(\"p\", null, \"If you have any questions, comments, or just want to say hello,\\n\", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://twitter.com/messages/compose?recipient_id=33618361\"\n  }), \"send me a message\"), \".\"), mdx(\"p\", null, \"Thanks for reading!\"));\n}\n;\nMDXContent.isMDXComponent = true;","frontmatter":{"title":"Comparing Enzyme with React Testing Library","date":"2020-09-21","author":"Adam Goth","preview":"Enzyme has long been a popular library for testing React applications. More recently, React Testing Library has been gaining popularity in Enzyme's place. In this post, we'll take a look at how the two compare.","keywords":["javascript","react","web development","testing","react testing library","enzyme"]},"timeToRead":5}},"pageContext":{"id":"6559a392-7920-5d04-af96-e6cfd8a834d5"}},"staticQueryHashes":["63159454"]}