5
Chapter 5

Test Automation

Chapter 5 introduces automated testing in the development process, focusing on User Acceptance Testing (UAT) using Cypress. It guides through setting up Cypress, writing and running end-to-end tests, and ensuring that new features work as expected without breaking existing functionality. The chapter emphasizes the importance of automated testing in collaborative and complex projects.

In this chapter, you will learn

  • Setting up and using Cypress for User Acceptance Testing.
  • Writing and running automated tests for the 'Quote of the Day' app.
  • Importance of automated testing in maintaining code integrity.

  • Test automation

    As we start incorporating new features, every modification to the code raises a question - how do we ensure that our changes don't break existing features? If only one person develops the entire application, this might not pose a significant problem. However, as the features become more complex and other team members join, ensuring that each change doesn't break the whole application becomes an issue that requires prior planning.

    In practice, we need to introduce automated testing to address this issue. We need to run tests against each modification, and if the tests fail, complete fixes must be made before submitting and syncing with the remote. Of course, automated testing can be broken down into different levels, such as user acceptance testing, integration testing, performance testing, security testing, and unit testing, etc. In this chapter, we need to discuss user acceptance testing and component testing.

    Acceptance Testing

    User Acceptance Testing (UAT) is the testing process in the final stage of the software development lifecycle, typically performed by the end-users or customers, to confirm whether the system or product meets the predetermined business requirements. Its main goal is to validate whether the software meets business needs and ensure the system can operate in real-world business scenarios.

    The importance of user acceptance testing lies in its provision of a mechanism to ensure that the software product is not only developed according to technical specifications but genuinely meets the users' actual needs. In other words, UAT provides an opportunity to confirm that the end-users can effectively use the software in real environments.

    Cypress is a popular end-to-end testing framework designed specifically for modern web applications. It allows developers to write code to test various aspects of their applications, from simple user interactions (like button clicks and form submissions) to more complex behaviors such as page navigation and dynamic content loading.

    Below are some key features of Cypress:

    • Real-time reloading: Cypress can rerun tests immediately after you save a test file, making the development process more efficient.
    • Automatic waiting: You don't need to add waits or sleeps in your tests; Cypress automatically waits for commands and assertions to complete.
    • Top-notch error messages: When tests fail, Cypress provides detailed error messages to help you understand what went wrong.
    • Network traffic control: Cypress allows you to stub and intercept network requests to simulate server behavior.

    With Cypress, you can perform user acceptance testing, simulate user interactions with your web application in a real browser environment, and verify whether the functionality and performance of the program meet expectations.

    To install cypress, as with other node.js packages, you only need to execute the following command.

    npm install cypress --save-dev

    After the installation is complete, we initialize Cypress. Cypress provides a wizard, and you need to run:

    ./node_modules/.bin/cypress open

    To start the configuration assistant:

    cypress init
    cypress init

    Here we choose E2E Testing, and for now, we can leave the rest at their default values. The assistant will generate some configuration files. At the final step, it prompts us to create a spec file, which is a test description file in Cypress. We can enter a filename, such as: quote-of-the-day.spec.cy.ts.

    This will add a cypress directory to our project directory:

    cypress ├── downloads ├── e2e │   └── quote-of-the-day.spec.cy.js ├── fixtures │   └── example.json └── support ├── commands.js └── e2e.js

    To execute quote-of-the-day.spec.cy.js, we need to enter the following command in the Terminal:

    node_modules/.bin/cypress run --spec cypress/e2e/quote-of-the-day.spec.cy.js

    This command uses cypress and specifies the test file to be run with the —spec option. Since this test is automatically generated by Cypress, it should yield a verification passed interface.

    end to end
    end to end

    If we look at the content of quote-of-the-day.spec.cy.js, we see the following code:

    quote-of-the-day.spec.cy.js
    describe('template spec', () => { it('passes', () => { cy.visit('https://example.cypress.io') }) })

    In this test case, cy.visit('https://example.cypress.io') signifies visiting the URL https://example.cypress.io. cy.visit is a function provided by Cypress, used to simulate a user visiting a page within a test. If the page loads successfully, the test will pass.

    Testing the "Quote of the Day"

    We can modify the test code provided to suit our needs. Our application runs locally at http://localhost:3000, and we want to verify whether a predefined quote is displayed:

    quote-of-the-day.spec.cy.js
    describe("quote of the day spec", () => { it("displays a quote", () => { cy.visit("http://localhost:3000"); cy.contains( "Any fool can write code that a computer can understand. Good programmers write code that humans can understand." ); cy.contains("Martin Fowler"); }); });

    cy.visit("http://localhost:3000") simulates a user visiting "localhost:3000", which is our local application. The cy.contains function checks whether a specified text content is present on the page. The two cy.contains functions check whether the quote content and the author are correctly displayed. If the expected texts are found on the page, the test will pass; otherwise, the test will fail.

    Note that this test accesses the local 3000 port through the browser, so we need to start our application with npm start in one Terminal window. Then, we execute the following in another Terminal:

    node_modules/.bin/cypress run --spec cypress/e2e/quote-of-the-day.spec.cy.js

    Of course, we can define a new task e2e in package.json:

    package.json
    "scripts": { "e2e": "cypress run --spec cypress/e2e/*.spec.cy.js", "build": "esbuild src/index.jsx --bundle --outfile=dist/main.js", "start": "http-server dist -p 3000", "watch": "esbuild --bundle src/index.jsx --outfile=dist/main.js --watch" },

    Now we only need to input npm run e2e to automatically complete the function verification.

    It is the time to take a quiz

    What is the primary role of Cypress in the application development process?

    Testing User Interaction

    Next, we add another test to simulate a user clicking the "next" button and validate that the quote on the page is updated to a new one:

    quote-of-the-day.spec.cy.js
    it("clicks next button", () => { cy.visit("http://localhost:3000"); cy.contains('button', 'next').click(); cy.contains( "Truth can only be found in one place: the code." ); cy.contains("Robert C. Martin"); });

    This test code simulates the user visiting http://localhost:3000 and interacting with the "next" button.

    • cy.visit("http://localhost:3000"); opens the application.
    • cy.contains('button', 'next').click(); finds a button with 'next' text on the page and simulates a click operation.
    • cy.contains("Truth can only be found in one place: the code."); and cy.contains("Robert C. Martin"); are assertions checking for the presence of the corresponding text content on the page.

    We now have four different tasks in our package.json scripts:

    package.json
    "scripts": { "e2e": "cypress run --spec cypress/e2e/*.spec.cy.js", "build": "esbuild src/index.jsx --bundle --outfile=dist/main.js", "start": "http-server dist -p 3000", "watch": "esbuild --bundle src/index.jsx --outfile=dist/main.js --watch" }

    Our four tasks are:

    • The "e2e" command runs all Cypress end-to-end test files located in the "cypress/e2e" directory.
    • The "build" command uses the esbuild tool to bundle the "src/index.jsx" file, with the result saved in the "dist/main.js" file.
    • The "start" command starts a service, serving the "dist" directory as the root directory of a static resource server running on port 3000.
    • The "watch" command also uses esbuild to bundle the "src/index.jsx" file, but includes the "--watch" option, which means esbuild will automatically re-bundle whenever the source file changes.

    Summary

    Typically, we would use libraries such as jest and react-testing-library to write unit tests in a React codebase. For the sake of clarity, we purposely left this out in our application. For more on test-driven development in front-end applications, you can refer to my free course: Test-Driven Development With React, as well as the book of the same name.

    Test-Driven Development with React and TypeScript

    Test-Driven Development with React and TypeScript

    Apply Test-Driven principles to create scalable and maintainable React applications. This book covers a wide range of topics, including setting up a testing environment and utilizing popular testing frameworks like Cypress, Jest, and the React Testing Library. It also delves into valuable refactoring techniques, as well as enhancing code maintainability and readability.

    5

    You have Completed Chapter 5

    You've successfully integrated automated testing into your development workflow, a crucial step in ensuring the reliability and functionality of your application as it grows.

    With a robust testing setup in place, we'll next explore advanced development techniques to further enhance our application.

    © 2023