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:
- Open the Download page
- Click the download button
- 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
- 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.
- 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.