{"id":364218,"date":"2022-04-22T11:45:38","date_gmt":"2022-04-22T18:45:38","guid":{"rendered":"https:\/\/css-tricks.com\/?p=364218"},"modified":"2022-04-22T11:45:40","modified_gmt":"2022-04-22T18:45:40","slug":"front-end-test-element-locators","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/front-end-test-element-locators\/","title":{"rendered":"Writing Strong Front-end Test Element Locators"},"content":{"rendered":"\n

Automated front-end tests are awesome. We can write a test with code to visit a page \u2014 or load up just a single component<\/a> \u2014 and have that test code click on things or type text like a user would, then make assertions about the state of the application after the interactions. This lets us confirm that everything described in the tests work as expected in the application.<\/p>\n\n\n\n

Since this post is about one of the building blocks of any automated UI tests, I don\u2019t assume too much prior knowledge. Feel free to skip the first couple of sections if you’re already familiar with the basics.<\/p>\n\n\n\n\n\n\n

Structure of a front-end test<\/h3>\n\n\n

There\u2019s a classic pattern that\u2019s useful to know when writing tests: Arrange<\/strong>, Act<\/strong>, Assert<\/strong>. In front-end tests, this translates to a test file that does the following:<\/p>\n\n\n\n

  1. Arrange: Get things ready for the test.<\/strong> Visit a certain page, or mount a certain component with the right props, mock some state, whatever.<\/li>
  2. Act: Do something to the application.<\/strong> Click a button, fill out a form, etc. Or not, for simple state-checks, we can skip this.<\/li>
  3. Assert: Check some stuff.<\/strong> Did submitting a form show a thank you message? Did it send the right data to the back end with a POST?<\/li><\/ol>\n\n\n\n

    In specifying what to interact with<\/em> and then later what to check<\/em> on<\/em> the page<\/em>, we can use various element locators<\/em> to target the parts of the DOM we need to use.<\/p>\n\n\n\n

    A locator<\/dfn> can be something like an element\u2019s ID, the text content of an element, or a CSS selector, like .blog-post<\/code> or even article > div.container > div > div > p:nth-child(12)<\/code>. Anything about an element that can identify that element to your test runner can be a locator. As you can probably already tell from that last CSS selector, locators come in many varieties.<\/p>\n\n\n\n

    We often evaluate locators in terms of being brittle<\/em> or stable.<\/em> In general, we want the most stable element locators possible so that our test can always find the element it needs, even if the code around the element is changing over time. That said, maximizing stability at all costs can lead to defensive test-writing that actually weakens the tests. We get the most value by having a combination of brittleness and stability that aligns with what we want our tests to care about.<\/p>\n\n\n\n

    In this way, element locators are like duct tape. They should be really strong in one direction, and tear easily in the other direction. Our tests should hold together and keep passing when unimportant changes are made to the application, but they should readily fail when important changes happen that contradict what we’ve specified in the test.<\/p>\n\n\n

    Beginner\u2019s guide to element locators in front-end testing<\/h3>\n\n\n

    First, let\u2019s pretend we are writing instructions for an actual person to do their job. A new gate inspector has just been hired at Gate Inspectors, Inc. You are their boss, and after everybody\u2019s been introduced you are supposed to give them instructions for inspecting their first gate. If you want them to be successful, you probably would not<\/em> write them a note like this:<\/p>\n\n\n\n

    Go past the yellow house, keep going \u2018til you hit the field where Mike\u2019s mother\u2019s friend\u2019s goat went missing that time, then turn left and tell me if the gate in front of the house across the street from you opens or not.<\/p><\/blockquote>\n\n\n\n

    Those directions are kind of like using a long CSS selector or XPath as a locator. It\u2019s brittle \u2014 and it\u2019s the “bad kind of brittle”. If the yellow house gets repainted and you repeat the steps, you can\u2019t find the gate anymore, and might decide to give up (or in this case, the test fails).<\/p>\n\n\n\n

    Likewise, if you don\u2019t know about Mike\u2019s mother\u2019s friend\u2019s goat situation, you can\u2019t stop at the right reference point to know what gate to check. This is exactly what makes the “bad kind of brittle” bad \u2014 the test can break for all kinds of reasons, and none of those reasons have anything to do with the usability of the gate.<\/p>\n\n\n\n

    So let\u2019s make a different front-end test, one that\u2019s much more stable. After all, legally in this area, all gates on a given road are supposed to have unique serial numbers from the maker:<\/p>\n\n\n\n

    Go to the gate with serial number 1234 and check if it opens.<\/p><\/blockquote>\n\n\n\n

    This is more like locating an element by its ID. It\u2019s more stable and it\u2019s only one step. All the points of failure from the last test have been removed. This test will only fail if the gate with that ID doesn\u2019t open as expected.<\/p>\n\n\n\n

    Now, as it turns out, though no two gates should<\/em> have the same ID on the same road, that\u2019s not actually enforced anywhere And one day, another gate on the road ends up with the same ID.<\/p>\n\n\n\n

    So the next time the newly hired gate inspector goes to test \u201cGate 1234,\u201d they find that other one first, and are now visiting the wrong house and checking the wrong thing. The test might fail, or worse: if that gate works as expected, the test still passes but it\u2019s not testing the intended subject. It provides false confidence. It would keep passing even if our original target gate was stolen in the middle of the night, by gate thieves.<\/p>\n\n\n\n

    After an incident like this, it\u2019s clear that IDs are not as stable as we thought. So, we do some next-level thinking and decide that, on the inside of the gate, we\u2019d like a special ID just for testing. We\u2019ll send out a tech to put the special ID on just this one gate. The new test instructions look like this:<\/p>\n\n\n\n

    Go to the gate with Test ID \u201cmy-favorite-gate\u201d and check if it opens.<\/p><\/blockquote>\n\n\n\n

    This one is like using the popular data-testid<\/code> attribute. Attributes like this are great because it is obvious in the code that they are intended for use by automated tests and shouldn\u2019t be changed or removed. As long as the gate has that attribute, you will always find the gate. Just like IDs, uniqueness is still not enforced, but it\u2019s a bit more likely.<\/p>\n\n\n\n

    This is about as far away from brittle as you can get, and it confirms the functionality of the gate. We don\u2019t depend on anything except the attribute we deliberately added for testing. But there\u2019s a bit of problem hiding here\u2026<\/p>\n\n\n\n

    This is a user interface test for the gate, but the locator is something that no user would ever use to find the gate.<\/strong><\/p>\n\n\n\n

    It\u2019s a missed opportunity because, in this imaginary county, it turns out gates are required to have the house number printed on them so that people can see the address. So, all gates should have an unique human-facing label, and if they don\u2019t, it\u2019s a problem in and of itself. <\/p>\n\n\n\n

    When locating the gate with the test ID, if it turns out that the gate has been repainted and the house number covered up, our test would still pass. But the whole point of the gate is for people to access the house. In other words, a working gate that a user can\u2019t find<\/em> should still be a test failure, and we want a locator that is capable of revealing this problem.<\/p>\n\n\n\n

    Here\u2019s another pass at this test instruction for the gate inspector on their first day:<\/p>\n\n\n\n

    Go to the gate for house number 40 and check if it opens.<\/p><\/blockquote>\n\n\n\n

    This one uses a locator that adds value<\/em> to the test: it depends on something users also depend on, which is the label for the gate. It adds back a potential reason for the test to fail before it reaches the interaction we want to actually test, which might seem bad at first glance. But in this case, because the locator is meaningful from a user\u2019s perspective, we shouldn\u2019t shrug this off as \u201cbrittle.\u201d If the gate can\u2019t be found by its label, it doesn\u2019t matter if it opens or not \u2014 this is is the “good kind of brittle”.<\/p>\n\n\n

    The DOM matters<\/h3>\n\n\n

    A lot of front-end testing advice tells us to avoid writing locators that depend on DOM structure. This means that developers can refactor components and pages over time and let the tests confirm that user-facing workflows haven\u2019t broken, without having to update tests to catch up to the new structure. This principle is useful, but I would tweak it a bit to say we ought to avoid writing locators that depend on irrelevant<\/em> DOM structure in our front-end testing.<\/p>\n\n\n\n

    For an application to function correctly, the DOM should reflect the nature and structure of the content that’s on the screen at any given time. One reason for this is accessibility. A DOM that\u2019s correct in this sense is much easier for assistive technology to parse properly and describe to users who aren\u2019t seeing the contents rendered by the browser. DOM structure and plain old HTML make a huge difference to the independence of users who rely on assistive technology.<\/p>\n\n\n\n

    Let\u2019s spin up a front-end test to submit something to the contact form of our app. We\u2019ll use Cypress<\/a> for this, but the principles of choosing locators strategically apply to all front-end testing frameworks that use the DOM for locating elements. Here we find elements, enter some test, submit the form, and verify the \u201cthank you\u201d state is reached:<\/p>\n\n\n\n

    \/\/ 👎 Not recommended\ncy.get('#name').type('Mark')\ncy.get('#comment').type('test comment')\ncy.get('.submit-btn').click()\ncy.get('.thank-you').should('be.visible')<\/code><\/pre>\n\n\n\n

    There are all kinds of implicit assertions happening in these four lines. cy.get()<\/code> is checking that the element exists in the DOM. The test will fail if the element doesn\u2019t exist after a certain time, while actions like type<\/code> and click<\/code> verify that the elements are visible, enabled, and unobstructed by something else before taking an action.<\/p>\n\n\n\n

    So, we get a lot \u201cfor free\u201d even in a simple test like this, but we\u2019ve also introduced some dependencies upon things we (and our users) don\u2019t really care about. The specific ID and classes that we are checking seem stable enough, especially compared to selectors like div.main > p:nth-child(3) > span.is-a-button<\/code> or whatever. Those long selectors are so specific that a minor change to the DOM could cause a test to fail because it can\u2019t find the element<\/em>, not because the functionality is broken.<\/p>\n\n\n\n

    But even our short selectors, like #name<\/code>, come with three problems:<\/p>\n\n\n\n

    1. The ID could be changed or removed in the code, causing the element to go overlooked, especially if the form might appear more than once on a page. A unique ID might need to be generated for each instance, and that\u2019s not something we can easily pre-fill into a test.<\/li>
    2. If there is more than one instance of a form on the page and they have the same ID, we need to decide which form to fill out.<\/li>
    3. We don\u2019t actually care what the ID is from a user perspective, so all the built-in assertions are kind of\u2026 not fully leveraged?<\/li><\/ol>\n\n\n\n

      For problems one and two, the recommended solution is often to use dedicated data attributes in our HTML that are added exclusively for testing. This is better because our tests no longer depend on the DOM structure, and as a developer modifies the code around a component, the tests will continue to pass without needing an update, as long as they keep the data-test=\"name-field\"<\/code> attached to the right input<\/code> element.<\/p>\n\n\n\n

      This doesn\u2019t address problem three though \u2014 we still have a front-end interaction test that depends on something that is meaningless to the user.<\/p>\n\n\n

      Meaningful locators for interactive elements<\/h3>\n\n\n

      Element locators are meaningful when they depend on something we actually want<\/em> to depend on because something about the locator is important to the user experience. In the case of interactive elements, I would argue that the best selector to use is the element\u2019s accessible name<\/a>. Something like this is ideal:<\/p>\n\n\n\n

      \/\/ 👍 Recommended\ncy.getByLabelText('Name').type('Mark')<\/code><\/pre>\n\n\n\n

      This example uses the byLabelText helper<\/a> from Cypress Testing Library<\/a>. (In fact, if you are using Testing Library in any form, it is probably already helping you write accessible locators like this.)

      This is useful because now the built-in checks (that we get for free through the cy.type()<\/code> command) include the accessibility of the form field. All interactive elements should have an accessible name that is exposed to assistive technology. This is how, for example, a screenreader user would know what the form field they are typing into is called in order to enter the needed information.<\/p>\n\n\n\n

      The way to provide this accessible name for a form field is usually through a label<\/code> element associated with the field by an ID. The getByLabelText<\/code> command from Cypress Testing Library verifies that the field is labeled appropriately, but also that the field itself is an element that\u2019s allowed to have a label. So, for example, the following HTML would correctly fail before the type()<\/code> command is attempted, because even though a label<\/code> is present, labeling a div<\/code> is invalid HTML:<\/p>\n\n\n\n

      <!-- 👎 Not recommended  -->\n<label for=\"my-custom-input\">Editable DIV element:<\/label>\n<div id=\"my-custom-input\" contenteditable=\"true\" \/><\/code><\/pre>\n\n\n\n

      Because this is invalid HTML, screenreader software could never associate this label with this field correctly. To fix this, we would update the markup to use a real input<\/code> element:<\/p>\n\n\n\n

      <!-- 👍 Recommended -->\n<label for=\"my-real-input\">Real input:<\/label>\n<input id=\"my-real-input\" type=\"text\" \/><\/code><\/pre>\n\n\n\n

      This is awesome. Now if the test fails at this point after edits made to the DOM, it\u2019s not because of an irrelevant structure changes to how elements are arranged, but because our edits did something to break<\/em> a part of DOM that our front-end tests explicitly care about, that would actually matter to users.<\/p>\n\n\n

      Meaningful locators for non-interactive elements<\/h3>\n\n\n

      For non-interactive elements, we should put on our thinking caps. Let\u2019s use a little bit of judgement before falling back on the data-cy<\/code> or data-test<\/code> attributes that will always be there for us if the DOM doesn\u2019t matter at all.<\/p>\n\n\n\n

      Before we dip into the generic locators, let’s remember: the state of the DOM is our Whole Thing™ as web developers (at least, I think it is). And the DOM drives the UX for everybody who is not experiencing the page visually. So a lot of the time, there might be some meaningful element that we could or should be using in the code that we can include in a test locator.<\/p>\n\n\n\n

      And if there’s not, because. say, the application code is all generic containers like div<\/code> and span<\/code>, we should consider fixing up the application code first, while adding the test. Otherwise there is a risk of having our tests actually specify<\/em> that the generic containers are expected and desired, making it a little harder for somebody to modify that component to be more accessible.<\/p>\n\n\n\n

      \n

      This topic opens up a can of worms about how accessibility works in an organization. Often, if nobody is talking about it and it’s not a part of the practice at our companies, we don’t take accessibility seriously<\/a> as front-end developers. But at the end of the day, we are supposed to be the experts in what is the “right markup” for design, and what to consider in deciding that. I discuss this side of things a lot more in my talk from Connect.Tech 2021, called “Researching and Writing Accessible Vue… Thingies”<\/a>.<\/p>\n<\/div><\/div>\n\n\n\n

      As we saw above, with the elements we traditionally think of as interactive,<\/em> there is a pretty good rule of thumb that\u2019s easy to build into our front-end tests: interactive elements should have perceivable labels correctly associated to the element.<\/strong> So anything interactive, when we test it, should be selected from the DOM using that required label.<\/p>\n\n\n\n

      For elements that we don\u2019t think of as interactive \u2014 like most content-rendering elements that display pieces of text of whatever, aside from some basic landmarks like main<\/code> \u2014 we wouldn\u2019t trigger any Lighthouse audit failures if we put the bulk of our non-interactive content into generic div<\/code> or span<\/code> containers. But the markup won\u2019t be very informative or helpful to assistive technology because it\u2019s not describing the nature<\/em> and structure<\/em> of the content to somebody who can\u2019t see it. (To see this taken to an extreme, check out Manuel Matuzovic’s excellent blog post, “Building the most inaccessible site possible with a perfect Lighthouse score.”<\/a>)<\/p>\n\n\n\n

      The HTML we render is where we communicate important contextual information to anybody who is not perceiving the content visually. The HTML is used to build the DOM, the DOM is used to create the browser\u2019s accessibility tree<\/a>, and the accessibility tree is the API that assistive technologies of all kinds can use to express the content and the actions that can be taken to a disabled person using our software. A screenreader is often the first example we think of, but the accessibility tree can also be used by other technology, like displays that turn webpages into Braille, among others.<\/p>\n\n\n\n

      Automated accessibility checks won\u2019t tell us if we\u2019ve really created the right HTML for the content. The “rightness” of the HTML a judgement call we are making developers about what information we think needs to be communicated in the accessibility tree.<\/p>\n\n\n\n

      Once we\u2019ve made that call, we can decide how much of that is suitable to bake into the automated front-end testing.<\/p>\n\n\n\n

      Let\u2019s say that we have decided that a container with the status<\/code> ARIA role<\/code> will hold the \u201cthank you\u201d and error messaging for a contact form. This might be nice so that the feedback for the form\u2019s success or failure can be announced by a screenreader. CSS classes of .thank-you<\/code> and .error<\/code> could be applied to control the visual state.<\/p>\n\n\n\n

      If we add this element and want to write a UI test for it, we might write an assertion like this after the test fills out the form and submits it:<\/p>\n\n\n\n

      \/\/ 👎 Not recommended\ncy.get('.thank-you').should('be.visible')<\/code><\/pre>\n\n\n\n

      Or even a test that uses a non-brittle but still meaningless selector like this:<\/p>\n\n\n\n

      \/\/ 👎 Not recommended\ncy.get('[data-testid=\"thank-you-message\"]').should('be.visible')<\/code><\/pre>\n\n\n\n

      Both could be rewritten using cy.contains()<\/code>:<\/p>\n\n\n\n

      \/\/ 👍 Recommended\ncy.contains('[role=\"status\"]', 'Thank you, we have received your message')\n  .should('be.visible')<\/code><\/pre>\n\n\n\n

      This would confirm that the expected text appeared and was inside the right kind of container. Compared to the previous test, this has much more value in terms of verifying actual functionality. If any part of this test fails, we\u2019d want to know, because both the message and the element selector are important to us and shouldn\u2019t be changed trivially.<\/p>\n\n\n\n

      We have definitely gained some coverage here without a lot of extra code, but we\u2019ve also introduced a different kind of brittleness. We have plain English strings in our tests, and that means if the \u201cthank you\u201d message changes to something like \u201cThank you for reaching out!\u201d this test suddenly fails. Same with all the other tests. A small change to how a label is written would require updating any test that targets elements using that label.<\/p>\n\n\n\n

      We can improve this by using the same source of truth for these strings in front-end tests as we do in our code. And if we currently have human-readable sentences embedded right there in the HTML of our components\u2026 well now we have a reason to pull that stuff out of there.<\/p>\n\n\n

      Human-readable strings might be the magic numbers of UI code<\/h3>\n\n\n

      A magic number<\/a><\/dfn> (or less-excitingly, an \u201cunnamed numerical constant\u201d) is that super-specific value you sometimes see in code that is important to the end result of a calculation, like a good old 1.023033<\/code> or something. But since that number is not unlabeled, its significance is unclear, and so it’s unclear what it\u2019s doing. Maybe it applies a tax. Maybe it compensates for some bug that we don\u2019t know about. Who knows?<\/p>\n\n\n\n

      Either way, the same is true for front-end testing and the general advice is to avoid magic numbers<\/a> because of their lack of clarity. Code reviews will often catch them and ask what the number is for. Can we move it into a constant? We also do the same thing if a value is to be reused multiple places. Rather than repeat the value everywhere, we can store it in a variable and use the variable as needed.<\/p>\n\n\n\n

      Writing user interfaces over the years, I\u2019ve come to see text content in HTML or template files as very similar to magic numbers in other contexts. We\u2019re putting absolute values all through our code, but in reality it might be more useful to store those values elsewhere and bring them in at build time (or even through an API depending on the situation).<\/p>\n\n\n\n

      There are a few reasons for this:<\/p>\n\n\n\n

      1. I used to work with clients who wanted to drive everything from a content management system. Content, even form labels and status messages, that didn\u2019t live in the CMS were to be avoided. Clients wanted full control so that content changes didn\u2019t require editing code and re-deploying the site. That makes sense; code and content are different concepts. <\/li>
      2. I\u2019ve worked in many multilingual codebases where all the text needs to be pulled in through some internationalization framework, like this:<\/li><\/ol>\n\n\n\n
        <label for=\"name\">\n  <!-- prints \"Name\" in English but something else in a different language -->\n  {{content[currentLanguage].contactForm.name}}\n<\/label><\/code><\/pre>\n\n\n\n
        1. As far as front-end testing goes, our UI tests are much more robust if, instead of checking for a specific \u201cthank you\u201d message we hardcode into the test, we do something like this:<\/li><\/ol>\n\n\n\n
          const text = content.en.contactFrom \/\/ we would do this once and all tests in the file can read from it\n\ncy.contains(text.nameLabel, '[role=\"status\"]').should('be.visible')<\/code><\/pre>\n\n\n\n

          Every situation is different, but having some system of constants for strings is a huge asset when writing robust UI tests, and I would recommend it. Then, if and when translation or dynamic content does become necessary for our situation, we are a lot better prepared for it.<\/p>\n\n\n\n

          I\u2019ve heard good arguments against<\/em> importing text strings in tests, too. For example, some find tests are more readable and generally better if the test itself specifies the expected content independently from the content source.<\/p>\n\n\n\n

          It’s a fair point. I\u2019m less persuaded by this because I think content should be controlled through more of an editorial review\/publishing model, and I want the test to check if the expected content from the source<\/em> got rendered, not some specific strings that were correct when the test was written. But plenty of people disagree with me on this, and I say as long as within a team the tradeoff is understood, either choice is acceptable.<\/p>\n\n\n\n

          That said, it\u2019s still a good idea to isolate code from content in the front end more generally. And sometimes it might even be valid to mix and match \u2014 like importing strings in our component tests and not importing them in our end-to-end tests. This way, we save some<\/em> duplication and gain confidence that our components display correct content, while still having front-end tests that independently assert the expected text, in the editorial, user-facing sense.<\/p>\n\n\n

          When to use data-test<\/code> locators<\/h3>\n\n\n

          CSS selectors like [data-test=\"success-message\"]<\/code> are still useful and can be very helpful when used in an intentional way, instead of used all the time. If our judgement is that there\u2019s no meaningful, accessible way to target an element, data-test<\/code> attributes are still the best option. They are much better than, say, depending on a coincidence like whatever the DOM structure happens to be on the day you are writing the test, and falling back to the “second list item in the third div<\/code> with a class of card<\/code>” style of test.<\/p>\n\n\n\n

          There are also times when content is expected to be dynamic and there\u2019s no way to easily grab strings from some common source of truth to use in our tests. In those situations, a data-test<\/code> attribute helps us reach the specific element we care about. It can still be combined with an accessibility-friendly assertion, for example:<\/p>\n\n\n\n

          cy.get('h2[data-test=\"intro-subheading\"]')<\/code><\/pre>\n\n\n\n

          Here we want to find what has the data-test<\/code> attribute of intro-subheading<\/code>, but still allow our test to assert that it should be a h2<\/code> element if that\u2019s what we expect it to be. The data-test<\/code> attribute is used to make sure we get the specific h2<\/code> we are interested in, not some other h2<\/code> that might be on the page, if for some reason the content<\/em> of that h2<\/code> can\u2019t be known at the time of the test.<\/p>\n\n\n\n

          Even in cases where we do know the content, we might still use data attributes to make sure the application renders that content in the right spot:<\/p>\n\n\n\n

          cy.contains('h2[data-test=\"intro-subheading\"]', 'Welcome to Testing!')<\/code><\/pre>\n\n\n\n

          data-test<\/code> selectors can also be a convenience to get down to a certain part of the page and then make assertions within that. This could look like the following:<\/p>\n\n\n\n

          cy.get('article[data-test=\"ablum-card-blur-great-escape\"]').within(() => {\n  cy.contains('h2', 'The Great Escape').should('be.visible')\n  cy.contains('p', '1995 Album by Blur').should('be.visible')\n  cy.get('[data-test=\"stars\"]').should('have.length', 5)\n})<\/code><\/pre>\n\n\n\n

          At that point we get into some nuance because there may very well be other good ways to target this content, it’s just an example. But at the end of the day, it\u2019s a good if worrying about details like that is where we are because at least we have some understanding of the accessibility features embedded in the HTML we are testing, and that we want to include those in our tests.<\/p>\n\n\n

          When the DOM matters, test it<\/h3>\n\n\n

          Front-end tests bring a lot more value to us if we are thoughtful about how we tell the tests what elements to interact with, and what to contents to expect. We should prefer accessible names to target interactive components, and we should include specific elements names, ARIA roles, etc., for non-interactive content \u2014 if those things are relevant to the functionality. These locators, when practical, create the right combination of strength and brittleness.<\/p>\n\n\n\n

          And of course, for everything else, there\u2019s data-test<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"

          Automated front-end tests are awesome. We can write a test with code to visit a page \u2014 or load up just a single component \u2014 and have that test code click on things or type text like a user would, then make assertions about the state of the application after the interactions. This lets us […]<\/p>\n","protected":false},"author":284425,"featured_media":364270,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"c2c_always_allow_admin_comments":false,"footnotes":"","jetpack_publicize_message":"Writing Strong Front-end Test Element Locators by @marktnoonan","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[4],"tags":[5209,529],"jetpack_publicize_connections":[],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/pass-fail.jpg?fit=1200%2C600&ssl=1","jetpack-related-posts":[{"id":286484,"url":"https:\/\/css-tricks.com\/model-based-testing-in-react-with-state-machines\/","url_meta":{"origin":364218,"position":0},"title":"Model-Based Testing in React with State Machines","date":"September 4, 2019","format":false,"excerpt":"Testing applications is crucially important to ensuring that the code is error-free and the logic requirements are met. However, writing tests manually is tedious and prone to human bias and error. Furthermore, maintenance can be a nightmare, especially when features are added or business logic is changed. We\u2019ll learn how\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2019\/08\/endtoend.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":341342,"url":"https:\/\/css-tricks.com\/front-end-testing-is-for-everyone\/","url_meta":{"origin":364218,"position":1},"title":"Front-End Testing is For Everyone","date":"June 1, 2021","format":false,"excerpt":"Testing is one of those things that you either get super excited about or kinda close your eyes and walk away. Whichever camp you fall into, I\u2019m here to tell you that front-end testing is for everyone. In fact, there are many types of tests and perhaps that is where\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/05\/24gR1Ckg.png?fit=1200%2C900&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":304287,"url":"https:\/\/css-tricks.com\/automated-selenium-testing-with-jest-and-lambdatest\/","url_meta":{"origin":364218,"position":2},"title":"Automated Selenium Testing with Jest and LambdaTest","date":"March 5, 2020","format":false,"excerpt":"You know what the best thing is about building and running automated browser tests is? It means that the site you're doing it on really matters. It means you're trying to take care of that site by making sure it doesn't break, and it's worth the time to put guards\u2026","rel":"","context":"In "Sponsored"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/02\/JPG.jpg?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":299235,"url":"https:\/\/css-tricks.com\/create-amazingly-stable-tests-your-way-coded-and-code-less\/","url_meta":{"origin":364218,"position":3},"title":"Create Amazingly Stable Tests Your Way \u2014 Coded and Code-Less","date":"November 21, 2019","format":false,"excerpt":"Testim\u2019s end-to-end test automation delivers the speed and stability of AI-based codeless tests, with the power of code. You get the flexibility to record or code tests, run on third-party grids, fit your workflow and tools including CI, Git and more. Join the Dev Kit beta to start writing stable\u2026","rel":"","context":"In "Sponsored"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2019\/11\/1200x600_v3@2x.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":157961,"url":"https:\/\/css-tricks.com\/continuous-integration-continuous-deployment\/","url_meta":{"origin":364218,"position":4},"title":"Why You Should Use Continuous Integration and Continuous Deployment","date":"December 9, 2013","format":false,"excerpt":"The following is a guest post by Florian Motlik. Florian is CTO of Codeship, a hosted Continuous Integration and Continuous Deployment platform. What the heck is that, you might ask? It's kind of a philosophy and toolset for working on websites. That is perhaps a bit too simplified, so I'll\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":353842,"url":"https:\/\/css-tricks.com\/testing-vue-components-with-cypress\/","url_meta":{"origin":364218,"position":5},"title":"Testing Vue Components With Cypress","date":"October 27, 2021","format":false,"excerpt":"Cypress is an automated test runner for browser-based applications and pages. I\u2019ve used it for years to write end-to-end tests for web projects, and was happy to see recently that individual component testing had come to Cypress. I work on a large enterprise Vue application, and we already use Cypress\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/10\/vue-cypress-testing-assembly-line.jpg?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]}],"featured_media_src_url":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/pass-fail.jpg?fit=1024%2C512&ssl=1","_links":{"self":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/364218"}],"collection":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/users\/284425"}],"replies":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/comments?post=364218"}],"version-history":[{"count":9,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/364218\/revisions"}],"predecessor-version":[{"id":365256,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/364218\/revisions\/365256"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media\/364270"}],"wp:attachment":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media?parent=364218"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/categories?post=364218"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/tags?post=364218"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}