Front-end testing: e2e testing

why test

Do you have the following troubles:

  • After you work overtime to complete a function, submit it to the testing department and immediately return several bugs

  • After you have modified the bug and checked it several times to make sure it is correct, submit it to the testing department and return a few bugs

    ……

For the above situation, have you ever had any doubts, why the inspection is all right or there is a bug? All of these are due to lack of testing.

You may ask, did it, and checked it several times. Indeed, you have tested, but you have not completed the closed loop of testing. You may complete some parts of the test and not others. Since you said I didn't complete the test, what is the test and how to conduct the test?

What is a test?

For the front end, testing is mainly to test HTML, CSS, and JavaScript to ensure the normal operation of the code.

Common tests include unit tests, integration tests, and end-to-end (e2e) tests.

  • Unit testing: testing the smallest testable unit in the program. We can compare it to the testing of a car. Before the car is assembled, the parts need to be tested. In this case, it is consistent with the unit test. Only when the parts are normal can the next step of assembly be carried out
  • Integration testing: Testing an entirety of executable units. Similar to the test of a car, it is equivalent to testing the engine as a whole by assembling the parts required for the engine before testing the engine.
  • End-to-end testing (e2e): testing from server to client. This kind of test is to test the entire system composed of the server and the client, and it is a test that can execute a complete process.

Now that we know that there are these types of tests, let's talk about how these tests should be implemented.

how to test

The testing methods can be divided into manual testing and automatic testing.

Manual testing: It is to let the personnel of the testing department operate according to the business process. When a problem occurs in a certain step or steps, it means that there is a problem with this part of the code. This test method has obvious shortcomings:

  1. It can only test the part that the tester can see, and cannot test the part that the tester cannot see. For example, some internal utility functions, logic codes, etc., these are likely to have problems.

Automated testing: Use the written code to test the code. This kind of test can make up for the lack of manual testing. Its granularity is at the code level and can accurately identify errors in a certain method.

Therefore, in the actual development process, we can use manual testing + automatic testing to test, and strive to cover 100% of the testing goals. Leaving aside manual testing, let's talk about automatic testing to achieve unit testing, integration testing, and e2e testing. This blog first talks about e2e testing.

e2e test

There are many libraries and frameworks for implementing e2e testing. This article uses Cypress as an example to explain.

Introduction to cypress

Cypress is a front-end automated testing tool based on the JavaScript language. It integrates a complete set of end-to-end testing methods without external tools. It can quickly, simply and reliably test all content running in the browser, and can perform interface test

Features of cypress

  • Time Travel: Cypress takes snapshots while tests are running. Just hover over the command log to get a clear picture of what happened every step of the way
  • Debuggability: No need to guess why tests fail. Debug directly using the browser's DevTools. Clear error causes and stack traces make debugging quick and easy
  • Real-time reload: every time the test code is changed, Cypress will execute new commands in real time and automatically reload the page for testing
  • Automatic waits: No need to add waits to your tests. Cypress will automatically wait for the element to load before executing the next command or assertion, asynchronous operations are no longer a problem
  • Spies, stubs and clocks: Cypress allows verification and control of function behavior, Mock server response or change system time for easier unit testing
  • Network flow control: Cypress can Mock the server to return the result, which can realize easy control without connecting to the back-end server, and simulate network requests
  • Consistency of running results: The Cypress architecture does not use Selenium or Webdriver, and has good guarantees in terms of running speed, reliability, and consistency of test results
  • Screenshots and videos: Cypress automatically takes screenshots when the test runs fail, and records the video of the entire test suite when running with commands, so you can easily grasp the test running status

cypress install

npm install cypress -D

After the installation is complete, execute npx cypress open will open a window to choose according to the framework and packager used by your project. After the selection is completed, the configuration file and its related files will be automatically generated, and then select the corresponding test type, here select e2e test. Once selected, the test suite can be run.

Cypress custom mount command

For vue projects, two plug-ins, vue-router and vuex, are generally used, so these two plug-ins need to be mounted on the vue instance during testing.

mount router

import {
    
    mount} from 'cypress/vue'

import router from '../../src/router';
Cypress.commands.add('mount',(component, options={
     
     })=>{
    
    
    options.global.plugins = options.global.plugins || [];
options.global.component = options.global.component || {
    
    };
    if(!options.router){
    
    
        options.router = router;
    }
    options.global.plugins.add({
    
    
        install(app){
    
    
           	app.use(options.router) ;
        }
    })
    return mount(component,options);
})

mount vuex

import {
    
    mount} from 'cypress/vue'
import store from '../../src/store';
Cypress.commands.add('mount',(component,options={
     
     })=>{
    
    
    options.global.plugins = options.global.plugins ||[];
    options.global.component = options.global.component || {
    
    };
    const {
    
    
        store = store,
        ...mountOption,
    } = options;
    options.global.plugins.push({
    
    
        install(app){
    
    
            app.use(store);
        }
    });
    return mount(component,mountOption);
})

Mount global components

import {
    
    mount} from 'cypress/vue'
import {
    
    BaseButton} from '../../src/components/BaseButton.vue'
Cypress.commands.add('mount',(component,options)=>{
    
    
	options.global.component =  options.global.component || {
    
    };
    options.global.component.BaseButton = BaseButton;
    return mount(component, options);
});

Configuration file (cypress.config.js)

Some commonly used configuration items are listed below, specific configuration instructions visit configuration instructions

const {
    
    defineConfig} = require('cypress');
module.exports = defineConfig({
    
    
  // e2e测试
  e2e:{
    
    
    
  },
  // 组件测试
  component:{
    
    
    
  },
  // 取消测试时录制视频
  video:false,
  // 取消测试失败时截屏
  screenshotOnRunFailure:false
});

grammar

The test file of the cypress framework is suffixed with .spec.js, and the test directory can be configured through cypress.config.js

The mount() method is used to mount components

import HelloWorld from './HelloWorld.vue';
describe('description',()=>{
    
    
  it('description',()=>{
    
    
		cy.mount(HelloWorld);
  });
});

The cy.contains() method is used to detect whether the content of the element in the page is the same as the specified content

it('测试 contains',()=>{
    
    
    cy.contains('hello world');
});

Browser Navigation Actions

cy.visit(url): visit the url, only the url that returns the html file can be accessed

cy.go():

cy.back():

cy.reload(boolean): true: no caching required, false: caching required

cy.url(): Get the url of the web page

cy.title(): Get the title of the webpage

element positioning

After running the test code, click the playground button, click the element with the mouse and copy the content of the input box at the top of the interface to get a line of code.

Subordinate relationship

within(): Find an element within the specified element

cy.get('form').within((form)=>{
    
    
    // 找form的第一个input
	cy.get('input:first').type('username');
})

context():

cy.context()

children(selector): find child elements

parent(): get the parent element of the element

sibling(): Find sibling elements

prev(): Find the previous element

let pwd_el = cy.get('.username').sibiling('input');
pwd_el.prev('input');

next(): Find the next element

let pwd_el = cy.get('.username').sibiling('input');
pwd_el.prev('input');

input operations for elements

focus(): input box focus event

cy.get('.password').focus().type('123');

blur(): input box out of focus event

cy.get('.username').blur();

submit(): Only the form form can be called

cy.get('.form').submit();

Element click action

click(): Click on the element

dbclick(): Double-click an element

rightclick(): Right click on the element

single and multiple choice

check(): Check the radio box or multi-selection box

cy.get('.radio').check();
cy.get('.checkbox').check();
// 强制选择,在禁用状态下依然可用
cy.get('.radio').check(true);
// 强制取消选择
cy.get('.radio').cehck(false);
// 选中其中的一个
cy.get('.checkbox').check('checkboxvalue');
cy.get('.radio').check('radiovalue');
// 选中其中的多个
cy.get('.checkbox').check('checkboxvalue1','checkboxvalue2');

drop down box

select(): Select the specified item in the drop-down box

// 通过选项的value值选中选项
cy.get('.select').select('selectvalue');
// 通过选项的文本值选中选项
cy.get('.select').select('selectText');
// 选中多个
cy.get('.multipleSelect').select(['selectText1','selectText2'])

window scrolling

scrollIntoView(): Scroll the element into the visible range

scrollTo(): ​​Scroll the window to the specified position

cy.get('.input').scrollIntoView();
// 滚动到顶端
cy.scrollTo('top');
// 滚动到最底部
cy.scrollTo('bottom');
// 滚动到指定位置
cy.scrollTo(100,200)

test pattern

If other styles are introduced in the html file in your project, you also need to introduce the same style in cypress/support/component-index.htmlz.

If you import other styles in the main.js of the project, you also need to import the same styles in cypress/support/component.js.

For example, my project uses the view-ui-plus third-party component library and introduces its style, so this style needs to be introduced in cypress/support/component.js. The component.js file under the cypress folder
of the main.js file in my project
insert image description here

insert image description here

test event

In the test file, provide a props for the component, and register the event name and event response function in the props

import Input from '../components/Input.vue'

describe('测试 input',()=>{
    
    
   it('测试 change',()=>{
    
    
       const changeHandler =  (v)=>{
    
    
           console.log(v);
       } 
       cy.mount(Input,{
    
    
           props:{
    
    
               onChange:changeHandler
           }
       });
       cy.get('.input').type('hello world');
       cy.get('@onChange').should('have.been.calledWidth',1);
   });
});

The principle of Cypress

  • Most testing tools (eg: Selenium/webdriver) run by running in an external browser and executing remote commands over the network
  • Because the underlying communication protocol of Webdriver is based on JSON Wire Protocol, network communication is required for operation
  • Cypress is the exact opposite of Webdriver, it executes in the same lifecycle as the application

The general flow of Cypress running tests

  1. After running the test, Cypress uses webpack to bundle all the modules in the test code into a js file
  2. Then, run the browser and inject the test code into a blank page, and then it will run the test code in the browser [** can be understood as: **Cypress puts the test code into an iframe to run ]
  3. When Cypress is first loaded for each test, the internal Cypress web application hosts itself locally on a random port **[eg: http://localhost:65874]**
  4. After recognizing the first command issued in the test, Cypress will change the local URL to match the Origin** of your remote application [satisfying the same-origin policy], which allows your test code and application to be in the same Run Loop running in **

Guess you like

Origin blog.csdn.net/qq_40850839/article/details/131013117