Frame structure
Frame structure
The framework is designed based on the PO model, splitting page elements and operations to reduce maintenance costs when the page is changed; at the same time, it uses xsd to customize xml tags and drives Selenium to execute by parsing xml, which reduces a certain amount of language learning costs.
The main function
- Concurrent execution based on selenium-grid
- Can be scripted based on xml
- Execution steps can insert custom functions
- Basic operations (click, hover, input, value comparison, switch iframe, switch page, close page, template matching, etc.)
- Test report collection
Design ideas
- Most of webUI automation is based on selenium
- PO model is the most widely used design pattern in webUI automation
- When selenium is executed, test data can be provided through DataProvider
- The common test data management methods are nothing more than the existence of db / the existence of local files, and they are all structured data
- Java has a DOM4J library, which can easily parse DOM documents. At the same time, XML in DOM can be flexibly defined and is relatively descriptive. Therefore, XML is selected as test data management so that there is a general running process of the framework.
- Initialize process data and basic configuration
- Initialize webDriver
- Traverse the parsed process data for execution
- Output test report
- Close webDriver
Obvious problems in the early stages of design
Q1: If you can only call built-in methods, the flexibility is not enough
A: Therefore, Spring is introduced as instance management, so that you only need to use the specified tag + beanName in the process data to implement the execution of the custom method.
Q2: WebUI automation usually takes too long to execute, how to shorten it?
A: Introduce selenium-grid for distributed execution and data isolation
Q3: Is there a more accurate and faster way to position elements?
A: Introducing image algorithms such as feature recognition, template matching, and OCR
Basic functions
XML description
Use self-describing XML for scripting, lowering barriers to entry
If you want to add a new tag/attribute, after defining and adding fields to the corresponding entity in the XSD, add a value in the AutoTestSuiteParser class.
Element reuse
Reference element files through ref tags[
Ready to use, repeated references enable reuse
Extraction and comparison
Obtain the corresponding value by specifying the attribute of the html tag, and store the variableName in the map of threadLocal in the form of kv, so that it can be extracted with $variableName in the subsequent process of the same testFlow and compared with the expected value
Template matching click
Using JavaCV for template matching and click operations
e.g.:
Just use the attribute templateImgName
The template image is stored under src/test/template_img, and the source image is a screenshot of the current browser window
Tips: When using template matching, it is not recommended to execute it locally as it will most likely fail because local operations may interfere with the original positioning of the mouse.
Concurrency and distributed execution
By reading the configuration in the engine.properties file through reflection, the number of concurrent execution use cases can be dynamically configured.
During concurrent execution, tasks will be sent to selenium-hub and distributed to speed up execution.
Execute custom method
When using the custom tag, specify the bean name of the customFunction specified class, and the class should implement the execute method of ICustomAction
The method bean name is beanName in _@_Service()
Common operation encapsulation
Encapsulates commonly used operations and can be expanded at the same time
Environment deployment
selenium-grid
m1 (arm64)
version: "3" services: selenium-hub: image: seleniarm/hub:4.0.0-beta-1-20210215 container_name: selenium-hub-arm ports: - "4444:4444" environment: - GRID_MAX_SESSION=50 - GRID_TIMEOUT=200 - START_XVFB=false - GRID_CLEAN_UP_CYCLE=200 chrome: image: seleniarm/node-chromium:4.0.0-beta-1-20210215 volumes: -/dev/shm:/dev/shm depends_on: -selenium-hub environment: - SE_EVENT_BUS_PUBLISH_PORT=4444 - SE_EVENT_BUS_HOST=selenium-hub - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 - NODE_MAX_INSTANCES=5 -NODE_MAX_SESSION=5 - GRID_CLEAN_UP_CYCLE=200
intel(x86)
version: "3" services: selenium-hub: image: selenium/hub container_name: selenium-hub ports: - "4444:4444" environment: - GRID_MAX_SESSION=50 - GRID_TIMEOUT=900 - START_XVFB=false chrome: image: selenium/node-chrome volumes: -/dev/shm:/dev/shm depends_on: -selenium-hub environment: - SE_EVENT_BUS_PUBLISH_PORT=4444 - SE_EVENT_BUS_HOST=selenium-hub - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 - NODE_MAX_INSTANCES=5 -NODE_MAX_SESSION=5 - GRID_CLEAN_UP_CYCLE=200
start.sh
#!/bin/sh docker-compose up -d --scale chrome=2
chrome=2 represents the number of nodes in the selenium-grid cluster, chrome is the service name in docker-compose
Start the shell script, docker ps to check whether the startup is successful
Visit localhost:4444 / virtual machine ip:4444
Extentx
Create a Dockerfile
FROM node:alpine LABEL maintainer="lain" RUN mkdir -p ./app WORKDIR ./app RUN apk add --no-cache git RUN git clone https://gist.github.com/3c4f35a41c58a55a0ffd00c3e64142c8.git tmpChange RUN git clone https://github.com/anshooarora/extentx.git RUN mv tmpChange/connections.js extentx/config/connections.js RUN rm -rf tmpChange WORKDIR ./extentx EXPOSE 1337 RUN npm set registry https://registry.npm.taobao.org RUN npm config set registry https://registry.npm.taobao.org RUN npm config set disturl https://npm.taobao.org/dist RUN npm install CMD ["./node_modules/.bin/sails", "lift"]
Build image
docker build -t extentx .
Create docker-compose.yml file
version: '3.6' services: extentx: build: . environment: - MONGODB_PORT_27017_TCP_ADDR=mongo links: -mongo:mongo ports: - 1337:1337 mongo: image:mongo:3.4 ports: - 27010:27017
docker-compose up -d
Start
Just visit localhost:1337
Package to local warehouse
Download extentsReport-1.0-SNAPSHOT.jar attached to the project, extract the pom file in the jar package, place it in the same directory as the jar, and execute the command
mvn install:install-file -Dfile=extentsReport-1.0-SNAPSHOT.jar -DartifactId=extentsReport -DgroupId=com.antigenmhc -Dversion=1.0-SNAPSHOT -Dpackaging=jar -DpomFile=pom.xml
That’s it
JavaCV (taking arm64 as an example)
mac m1 (arm64)
The pre-environment is the downloaded arm64 version of JDK
- Install and set environment variables
# Upgrade brew brew update # Install cmake brew install cmake # Install ant brew installant export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_331.jdk/Contents/Home export PATH=$JAVA_HOME/bin:$PATH export JAVA_AWT_INCLUDE_PATH=$JAVA_HOME export JAVA_AWT_LIBRARY=$JAVA_HOME export JAVA_INCLUDE_PATH=$JAVA_HOME/indclude export JAVA_INCLUDE_PATH2=$JAVA_HOME/include/darwin export JAVA_JVM_LIBRARY=$JAVA_HOME
- Open Java build
brew edit opencv
turn up
-DBUILD_opencv_java=OFF
changed to -DBUILD_opencv_java=ON
Add the parameter after the above parameters: -DOPENCV_JAVA_TARGET_VERSION=1.8
to prevent compilation using a higher version of JDK
- compile
brew install --build-from-source opencv
- Generate jar and dylib: After success, libopencv_java470.dylib and opencv-470.jar will be generated in /opt/homebrew/Cellar/opencv/4.7.0/share/java/opencv4
- Add jar and dylib to the project
Set the project JDK to the JDK just used to edit (i.e. JAVA\_HOME)
- Introduce dependencies
<dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>1.5.7</version> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacpp</artifactId> <version>1.5.7</version> </dependency>
- run demo
//Picture to be matched Mat src = imread("filePath",Imgcodecs.IMREAD_GRAYSCALE); Mat mInput=src.clone(); // Get matching template Mat mTemplate = imread("filePath",Imgcodecs.IMREAD_GRAYSCALE); /** * TM_SQDIFF = 0, squared difference matching method, the best match is 0, the larger the value, the worse the match. * TM_SQDIFF_NORMED = 1, normalized squared difference matching method * TM_CCORR = 2, correlation matching method, using multiplication operation, the larger the value, the better the match. * TM_CCORR_NORMED = 3, normalized correlation matching method * TM_CCOEFF = 4, correlation coefficient matching method, the best match is 1, -1 means the worst match * TM_CCOEFF_NORMED = 5; Normalized correlation coefficient matching method */ int resultRows = mInput.rows() - mTemplate.rows() + 1; int resultCols = mInput.cols() - mTemplate.cols() + 1; Mat gResult = new Mat(resultRows, resultCols, CvType.CV_32FC1); Imgproc.matchTemplate(mInput, mTemplate, gResult, Imgproc.TM_CCORR_NORMED); Core.normalize(gResult, gResult, 0, 1, Core.NORM_MINMAX, -1, new Mat()); Core.MinMaxLocResult mmlr = Core.minMaxLoc(gResult); Point matchLocation = mmlr.maxLoc; double x = matchLocation.x + (mTemplate.cols() / 2); double y = matchLocation.y + (mTemplate.rows() / 2); System.out.println(new Point(x, y));
The console outputs the coordinates successfully.
Run the project
Project structure and description
The general structure of the project is as follows
qa-ui-test-demo ├─ screen: screenshot ├─ src │ ├─ main │ │ └─ resources │ │ ├─ css │ │ ├─ driver: browser driver │ │ │ ├─ chromedriver │ │ │ └─ geckodriver │ │ ├─ cc.properties: Email sender’s email address │ │ ├─ extentx.properties: connect extentx platform configuration │ │ ├─ engine.properties: test report and core configuration │ │ ├─ mail.properties: Mail configuration │ │ └─ sendto.properties: Email of the email recipient │ └─ test │ ├─ java │ │ └─ com │ │ └─ qalain │ │ └─ ui │ │ └─ action: java script │ └─ resources │ ├─ page: page xml, used to convert to page pojo │ │ └─ demo-baidu.xml │ ├─ suiteflow: automated test process file │ │ └─ demo-flow.xml │ └─ suite-testng.xml ├─ pom.xml └─ README.md
Because it has been encapsulated twice in xml, you need to understand common tags
- When action is openPage, the attribute that needs to be set is url;
- When the action is click, the attribute that needs to be set is refId;
- When action is fillvalue, the attributes that need to be set are elementId and value;
- When action is compareValue, the properties that need to be set are refId and expectValue;
- When action is keyBoardEnter, no other attributes need to be set;
- When action is swithWindow, there is no need to set other attributes;
- When the action is closeCurrentWindow, there is no need to set other properties;
- When action is closeDrive, no other attributes need to be set;
- When action is jsInvoker, the attribute that needs to be set is jsCode;
- When action is custom, the attribute that needs to be set is customFunction;
- When action is hover, there is no need to set other attributes
Recommended Python automated testing learning videos
[Check-in WebUI automated testing] From Selenium principles → element positioning → design mode → keyword drive → Excel data-driven implementation effect,…
Record the entire process of the UI automated testing framework from building to test report generation: design mode → keyword-driven design → Excel data-driven design → generation…