The cause of

In the process of a buried demand, in order to facilitate the test, deleted the download function of the official website, forgot to change back to the online. Because there was no functional development, there were more pages involved and the product was just reported as a play, and no one found it because it had not been tested.

The business of the official website is relatively simple, the meaning of existence is to introduce the product, provide the address for users to download the product, the purpose of introducing the product is also to let users to download and use, once the small download function strikes, it directly means the loss of customers. In order to ensure that these core but simple features that did not attract the attention of test colleagues were not lost during iterations, we decided to introduce automated testing into the project.

Why choose Cypress

Cypress is the front-end E2E testing framework, and E2E is end-to-end. The features I want to test are simple: click download client and user submit message. From the point of view of the user and the test, they don’t care what framework or logic is used in the front end, they just want to know the interaction effect on the browser, whether the UI display effect is correct, whether the function is used correctly, and test according to this way of thinking, also called E2E test.

Aside from business scenarios, I chose it primarily because it is based on Node JS and is quick for front-end developers.

use

The installation

Go to the project directory where you want to access Cypress

cd /your/project/path 
Copy the code

To install Cypress, I’m using NPM to download the dependency. The version number is 8.1.0

npm install cypress --save-dev
Copy the code

Run the cypress open command on the VS Code terminal to enable Cypress

./node_modules/.bin/cypress open
Copy the code

Cypress folder

After the open command is successfully executed, a small window is displayed. The 1-getting-started and “2-advanced-examples” directories contain the built-in test cases.

At the same time, there will be more Cypress folders in the project:

  • Fixtures: Place to store test data, interpreted as a place to place interface mocks
  • Integration: General test cases are written under Integration and come with official sample test case files.
  • Plugins: Store plug-ins, either self-written or third-party, that are executed in Node before the project loads, before the browser starts, and during test execution.
  • Support: the cypress/support/index.js file is run before each specification file. For example, some actions are globally applicable, so they can be placed here. For example, add the following code to cypress/support/index.js:
BeforeEach (() => {cy. Log (' run beforeEach test execution ')}) // after running, you see a log output for each test case.Copy the code

When you run a test in Cypress Run mode, cypress automatically takes screenshots /screenshots and saves them in cypress/ ScreenShots and screenshots in Cypress/Video by default when an error occurs

Writing test cases

Think about the process of testing and clicking download:

  1. Open the Download page
  2. Click the download button
  1. Wait until the client download is complete

The rest of the steps, the bug is not the front end of the classmate’s pot.

// cypress/integration/download.js const path = require("path"); describe('The Home Page', () => { it('successfully visit', () => { cy.visit('http://localhost:3001') }) it('successfully download', () => {cy. Contains (' download ').click() // After the test, the cypress folder will have more downloads directory. Const downloadsFolder = cypress.config ("downloadsFolder"); Const downloadedFilename = path.join(downloadsFolder, "download.exe "); ReadFile (downloadedFilename).then(data => {// execute assertion}); })})Copy the code

After you save the test file, your test JS file will appear in the small window of Cypress. Click to open the test

As shown in the figure below, the test of accessing the download page and downloading client is successful. The test log is on the left of the interface, the download page is displayed in the middle, and the browser console is on the right:

The problem is that it can take a long time to detect if the user has clicked to download. I didn’t want the test to take that long, so I took another approach: proxy the interface where the page gets the download link to the local mock, and get the link of the A tag to compare with the mock data.

Describe ('The Home Page', () => {before(() => {cy.intercept('GET', '/ download.php ', {fixture: 'download.json' }) }) it('successfully loads', () => { cy.visit('http://localhost:3001') }) it('successfully request download api', Fixture ('downloadv2.json').then(({data = []}) => {// cypress gets the node a tag with the data-lBL attribute Cy. Get (` [download data - LBL =] `). Should (' have. Attr ', 'href'), then (href = > {expect (href). To. Eq (config. FUrl)})})})})Copy the code

Cypress API

Here are some of the common apis I encountered when I came across the test cases. Although the official website provides guidance, I was a little confused when I first came across the basic concepts. Cypress website API document portal

Test suites and use cases

Describe () and IT () are two essential components of an executable test case

  • Describe: represents test suite, which can be specified. A test suite may not contain any Hook functions, but must contain at least one test case it(), which can be nested with subtest suites
  • It: represents a test case

Context: an alias for describe(), which has the same behavior. You can use context() instead of describe()

Hook function

  • Before () : When running cypress via cypress open, this event is triggered when the project is opened. This event is triggered each time a Cypress Run is executed. In my test case above, I proxyed the interface to the mock data before the test, so that when the visit() interface opened the page, the mock data was rendered on the node.

Before is run before the first use case, and afeter is run after all the use cases have been run. BeforeEach is run beforeEach use case and afterEach is run afterEach use case.

Find and manipulate DOM nodes

  • Get (): Used to find the DOM element in the DOM tree. The get method can be used to find the corresponding DOM by selector like jquery.

There are other lookup methods, such as: children obtains the child elements of each DOM element in a set of DOM elements, parent obtains the parent DOM elements of a set of DOM elements, and siblings obtains the sibling DOM elements.

  • Trigger (): Fires an event on a DOM element. Such as:
Dom.trigger ('mouseover') // Syntax example // eventName (string) The name of the event to be triggered on the DOM element. .trigger(eventName) // Position (string) // The position where the event should be triggered. The center location is the default location. // The valid positions are topLeft, top, topRight, left, center, right, bottomLeft, bottom, and bottomRight. .trigger(eventName, position) // options: Pass the option object to change the default behavior.trigger(eventName, options) // x (number) : The distance (in px) from the left of the element to the event that fires. // y (number) : the distance (px) from the top of the element to the event. .trigger(eventName, x, y) .trigger(eventName, position, options) .trigger(eventName, x, y, options)Copy the code

The network interface

  • Intercept: Manages the behavior of HTTP requests at the network layer

In my test case above, I used this API to intercept requests and proxy my local mock data

cy.intercept(url, staticResponse)
cy.intercept(method, url, staticResponse)
cy.intercept(routeMatcher, staticResponse)
cy.intercept(url, routeMatcher, staticResponse)
Copy the code

Actions Behavior events

The UI automates elements on the page by typing text, clicking on the element, clearing the text, and clicking on the button. There are also special checkboxes, radios, scrollbars, etc. Cypress is API accessible

  • Type (): Enters a text element into the input box. Focus (): Focuses on DOM elements. Clear (): clears DOM elements. Rightclick (): rightclick on the DOM element select(): select option box

How to automate

There are two ideas here. One is to place the tests locally earlier and trigger automatic tests during the commit phase of the test branch merge code, so that the problem can be thrown earlier. The second is to plug into the CI/CD, freeing up the deployment without waiting for testing. I first tried triggering during the COMMIT phase.

Add the NPM script

Specifies which test cases to execute. Otherwise, all tests are executed by default.

// package.json "scripts": { "test": "cypress run --spec cypress/integration/download.js", "pretest": "Perform the package here and run the service program ",},Copy the code

Add git hooks

I’m using Husky to create git commit hooks

Download Husky, the version I’m currently using is 7.0.1:

npm install husky --save-dev
Copy the code

Add the prepare script to packgae.json. The prepare script will be executed automatically after NPM install (without parameters). Husky install creates the.husky/ directory and specifies it to be the same directory as the git hooks directory:

npm set-script prepare "husky install"
npm run prepare
Copy the code

Add a hook for COMMIT:

npx husky add .husky/pre-commit "npm test"
git add .husky/pre-commit
Copy the code

This is implemented, and the test case is executed during each Git commit phase.

Existing problems

  1. Submitting the code on the VS Code terminal works fine for testing, but using VS Code’s source code management tools causes errors. Husky’s similar issue is still open. It is said that it can be solved by downgrading the version.

  1. Triggering during the COMMIT phase increases the waiting time for development, so subsequent attempts are made to connect the tests to the pipeline, as asynchronously as these operations are packaged and deployed.

I will continue to update the progress of new attempts in the future, and your comments are welcome.