This article is by IMWeb IMWeb team

A, origin – why to carry out small program automation test

Wechat mini program ecosystem is improving day by day. Many mini program projects have more and more pages, more and more complex structures, and more diversified business logic. Take Tencent Classroom mini program as an example. The page structure of part of Tencent Classroom mini program and its performance in different business scenarios are shown in the figure below:

As can be seen from the core function, the main page has many different performances for different business scenarios. Therefore, during the development and release process, a large number of test cases need to be manually verified to ensure that the small program works as expected. Programmers who are good at using tools will naturally think:

Can this repetitive work be left to a program to do automatically?

There are many automated solutions to this kind of testing problem in Web development, such as Selenium and Puppeteer. The idea is that the browser automatically clicks and types on the page in a specified order, and then compares the page performance with the desired result to get the test conclusion (assertion). Is there a way in the applet to automate along these lines and provide page information for assertion? In order to consider the underlying security of wechat, the miniprogram environment has always been relatively closed, leaving little room for developers to operate, and automatic operation is basically impossible to achieve. However, the miniprogram-automator tool appeared at the end of may, giving small program developers hope.

Ii. Chance encounter – Preliminary test of miniprogram- Automator

To summarize, when the automation interface of the developer wechat Developer tool is opened by command and connected to the automation interface, the tool provides the following capabilities:

  • MiniProgram: Get applets information (page stack, system information, page content), control applets (jump page, switch TAB, call method)
  • Page: Get Page information (path, element, data, structure), control the Page (set render data, call methods)
  • Element: Get Element information (attribute, style, content, position), manipulate elements (click, hold down, call methods)

Therefore, the realization of automatic control of small program depends on the development version of small program developer tools and miniprogram-automator tools. The applets developer tool command line is used to open the specified automation operation service port. (Developer tools version later than V1.02.1906042). The miniProgram-automator tool is used to manipulate small programs running in the developer tools and get the required information. For testing requirements, the JEST framework can be used to organize and assert test cases.

Without further ado, after reading the document, use:

Call the developer tools command line to open the project and specify the automated operation service port

PS D:\programs\ internal test \ wechat web developer tools >./cli.bat --auto D:\ appellatep \testMiniprogram --auto-port 9420 Initializing... IdePortFile: C:\Users\ billCui \AppData\Local\ wechat developer tools \User Data\Default\ ide starting IDE... IDE server has started, Listening on http://127.0.0.1:35510 initialization finished Open project with automation enabled success D:\ downloadp \testMiniprogram
Copy the code

Note the following in this command:

  1. The documentation requires that the developer tool version must be higher than V1.02.1906042, preferably the latest internal beta version of the tool, which I successfully ran on V1.03.1906062;
  2. To run this command, open the Developer Tools menu Settings -> Security Settings -> Service Port;
  3. The automation port is independent of the service port (for example, 35510 printed by the terminal is actually a service port) and must be seenOpen project with automation enabled success D:\weApp\testMiniprogramThis line prompts you to successfully open the automated port (9420).

After the command is successfully executed, the developer tool automatically opens the project and a message is displayed

Ø npm i miniprogram-automator --save-devInstall SDK, create test.js, introduce miniprogram-automator tool into the code, connect automatic operation port

const automator = require('miniprogram-automator');

const miniProgram = automator.connect({
  wsEndpoint: 'ws://localhost:9420',})Copy the code

ø Use the interface provided by miniprogram-automator to operate small programs from the home page restart and related operations

const automator = require('miniprogram-automator');

const miniProgram = automator.connect({
  wsEndpoint: 'ws://localhost:9420',
}).then(async miniProgram => {
  // Restart from the home page
  const page = await miniProgram.reLaunch('/pages/index/index');
  // Get the bottom-button component from the page
  const button = await page.$('bottom-button');
  // Print out the WXML information for button
  console.log(await button.wxml());
}).catch(e= > {
  console.log('catch a error', e);
});
Copy the code

The miniProgram-Automator was used to obtain the relevant information of the page after the operation, and jEST was used to organize and assert

// index.spec.js
const automator = require('miniprogram-automator');

describe('Classroom small program Automation Test', () = > {let miniProgram;
  // Called before running the test
  beforeAll(async () => {
    miniProgram = await automator.connect({
      wsEndpoint: 'ws://localhost:9420'}); });// called after running the test
  afterAll((a)= > {
    miniProgram.disconnect();
  });
  // Test the content
  it('nohost testing'.async() = > {const page = await miniProgram.reLaunch('/pages/index/index');
    const nohostButton = await page.$('nohost');
    expect(nohostButton).toBeNull();
  });
});
Copy the code

Run jest index.spec.js, if the nohost component does not exist in the page, the test passes, as shown in the figure:

Yuanju-application of automated test in classroom wechat applet

The introduction of automated testing in Wechat mini program of Tencent Classroom is mainly to solve the problem that the development, pre-release environment and formal environment need to open the course page of use cases repeatedly, which is cumbersome to operate and consumes a lot of manpower. As for the small program checklist in class, automated test procedures should be used as far as possible to complete test verification, so as to reduce manual operation and avoid the omission of human detection.

Using miniProgram-Automator tool and JEST framework, automatic test is mainly capable of simulating the operation of opening the specified page, clicking, scrolling and setting the data rendering data of the page in the specified order, and then predicate the specific page structure, data, component attributes and other information to judge whether it meets the expectation.

The following takes the course details page of Wechat mini program of Tencent Classroom as an example to explain in detail how to realize automated testing in actual projects:

The UI of the course details page is mainly divided into the video part, the details part and the purchase button at the bottom. If no course is purchased, the paid course details page is shown as follows:

Suppose the test objectives of the paid course details page for unpurchased undiscounted activities are as follows:

  1. The button should display “Buy now”, click the buy button to jump to the payment page
  2. Click the trial button to play the trial video normally
  3. Clicking the course video without purchasing the course cannot play

To implement this test, in x.pec. Js file, first introduce miniProgram-automator as described above, and connect the wechat applet project with automation port opened in beforeAll. (I won’t repeat the code here, see the previous chapter.) Let’s go straight to the code for the test content.

  1. Button display and click to jump to pay page test
   // Open the page and pass the parameter through the URL
   const page = await miniProgram.reLaunch(`/pages/course/course? cid=${commonPayCid}`);
   // Get button component information
   const basicApplyButton = await page.$('.basic--buy');
   // Determine what the button displays
   expect(await basicApplyButton.wxml()).toContain('Buy now'); 
   // simulate button clicking
   await basicApplyButton.tap();
   // Wait for the page to jump
   await page.waitFor(1500);
   // Get the current page path
   const currentPage = await miniProgram.currentPage();
   // Check whether the path is correct after the jump
   expect(currentPage.path).toContain('pages/order/order');
   // Jump back
   await miniProgram.navigateBack();
Copy the code

Currently miniProgram-automator provides two methods to get components from a page: page.$() and page.? (a)

Through experiments, we found that both selectors support selecting components by component name and class name, but for the internal structure of a custom component, it cannot be directly obtained.

The button at the bottom of the course details page is actually a custom component with nested child custom components. Let’s take a look at the WXML structure of the bottom button:

$(‘.bottom-btn’) or page.$(‘.buy’) returns undefined. Let’s take a look inside the bottom-button first.

const basicApplyButton = await page.$('bottom-button');
console.log(await basicApplyButton.wxml());
Copy the code

Get the bottom-button and print its WXML string and look:

// The output is actually a string, formatted for display<view class="bottom-button--bottom-button-space" wx:nodeid="17">
    <view class="bottom-button--bottom-button-wrapper" wx:nodeid="261">
        <basic is="components/discount-button/components/basic/basic" wx:nodeid="262">
            <view wx:nodeid="263">
                <view class="basic--bottom-button-container" wx:nodeid="264">
                    <view class="basic--bottom-btn basic--buy" wx:nodeid="265">Buy now</view>
                </view>
            </view>
        </basic>
    </view>
</view>
Copy the code

What did you find?! $(‘.basic–buy’); $(‘.basic–buy’); $(‘.basic–buy’); But the internal structure of a custom component actually exists in the Page in some way.

Look at the next jump, can direct access to the corresponding component after the call. The tap () method to simulate the click, here it is important to note that because of the small WeChat click to open a new page in application developer tools takes longer, need to wait for page load for a while, otherwise the access to the path of the current page when the page hasn’t jump in the past, we won’t be able to get a new page path. The waiting time can be given as a rule of thumb a slightly larger and safer value.

  1. Click the trial button to play the trial video normally
const player_video = await tapTcplayer(page, '.player-task');
expect(await player_video.wxml()).toContain('video-current-time'); / / try to learn
Copy the code

Due to the limitation of wechat developer tools, cloud vod will be downgraded to TCPlayer. The core component of TCPlayer is actually

How to determine whether the video is successfully played?

Let’s take a look at the WXML string of the successful video component by following the above method

   "<video class="component-video-video--player_video" controls="" danmu-list="[]" initial-time="0" object-fit="contain" poster="https://10.url.cn/qqc..." src="http://113.96.98.148/vedu.tc.qq.com/AtmkzyWCuq..." autoplay="" wx:nodeid="446"><div class="video-container" wx:nodeid="447"><div class="video-bar full" style="opacity: 1;" wx:nodeid="457"><div class="video-controls" wx:nodeid="458"><div class="video-control-button pause" wx:nodeid="459"><div parse-text-content="" class="video-current-time" wx:nodeid="460">00:02<div class="video-progress-container" wx:nodeid="462"><div class="video-progress" wx:nodeid="463"><div style="left: -21px;" class="video-ball" wx:nodeid="464"><div class="video-inner" wx:nodeid="465"><div parse-text-content="" class="video-duration" wx:nodeid="466">06:09<div class="video-fullscreen" wx:nodeid="468"><div style="z-index: -9999" class="video-danmu" wx:nodeid="453"></video>"
Copy the code

Surprised! Native < video > components, unexpectedly is < div >, we can also note that one of the key class: video – current – time values for 00:02 inside, this is not the current progress? Just enough to tell if the video is playing successfully. Here it is!

By comparison, it is found that no div with class video-current-time will appear when the playback fails. Therefore, it is directly determined whether the div contains video-current-time.

  1. Clicking the course video without purchasing the course cannot play

The video cannot play when you click on a non-trial course. Since only the cover image is displayed on the page when no video is played and no

async function tapTcplayer(page, className = '.task-item') {
     const taskItem = await page.$(className);
     await taskItem.tap();
     await page.waitFor(3000);
     const playercover = await page.$('.player-cover');
     const player_video = await playercover.$('.component-video-video--player_video');
     return player_video;
   }
   it('Paid course details page button display, jump, on-demand, trial function test'.async() = > {const page = await miniProgram.reLaunch(`/pages/course/course? cid=${commonPayCid}`);
       const basicApplyButton = await page.$('.basic--buy');
       expect(await basicApplyButton.wxml()).toContain('Buy now'); // Button display
       await basicApplyButton.tap();
       await page.waitFor(1500);
       const currentPage = await miniProgram.currentPage();
       expect(currentPage.path).toContain('pages/order/order');
       await miniProgram.navigateBack();
       const player_video = await tapTcplayer(page);
       expect(player_video).toBeNull(); // No video can be played without registration
       const player_video_new = await tapTcplayer(page, '.player-task');
       expect(await player_video_new.wxml()).toContain('current'); / / try to learn
     }, 20000);
Copy the code

You can see that the playback function was tested first, and then the trial function was tested. Why is that?

Because the showModel popup is not in the WXML structure and cannot be closed with the automatic control tool, in the actual test this popup will block the first step of the next test item: As a result, the next test item fails to open the page, and the next test item will not be affected if the popup window is directly placed before the test learning function.

Another point to note is that in the project, if the method of not triggering the progress refresh within 5 seconds after the play is clicked, the video will be reported as failing to play. The actual test found that the video can normally play in 3 seconds, so it only needs to wait for 3 seconds. If the video fails to play after 3 seconds, it is regarded as failing to play.

Finally, by default, the duration of a test item in Jest cannot be more than 5 seconds. This test has both page jump and video play, which obviously exceeds the limit of 5 seconds. The actual time is about 15 seconds, so the time limit is modified to 20000 milliseconds.

The result of running the test script is as follows:

The current test functions are as follows:

  • Nohost detection
  • Home page data pull, display, jump test
  • Paid course details page button display, jump, on-demand, trial learning function test
  • Coupon button display, receive function test
  • Limited time offer button shows test
  • Free course details page button display, registration, vod function test
  • Classification page display, jump list page, jump details page test

The completion of functional tests in the Checklist is as follows: The completion degree is 65%

Review some Automated testing note
Whether to remove the nohost plug-in support
Check whether the home page is displayed properly support
PC home page small program login is normal Temporarily not Information authorization cannot be completed automatically
Android pay ability is normal Temporarily not Information cannot be retrieved from inside the WebView
Whether the category page is displayed properly support
Check whether you can log in normally Temporarily not Information authorization cannot be completed automatically
Whether the class schedule is displayed normally, and whether the learning progress/live broadcast status is displayed normally support To be perfect
Whether the course details page can be displayed properly support
Scan/share correctly arouses the applet Temporarily not Developer tools are not supported
Whether the paid class live broadcast can be played normally (Shangyun and Tencent Video) Temporarily not Developer tools do not support live streaming
Can the free class live broadcast be played normally? (Shangyun and Tencent Video) Temporarily not Developer tools do not support live streaming
Can the free class recording be played normally? (Shanghai Cloud and Tencent Video) Part of the support Developer tools demoted to TCPlayer
Can paid lesson recording be played normally? (Shangyun and Tencent Video) Part of the support Developer tools demoted to TCPlayer
Try to see if the task can play normally support
Details page Whether the video can be played normally support
Whether the display of marketing tools is normal support
Whether payment logic can be completed properly Temporarily not Information cannot be retrieved from inside the WebView
Whether category screening is normal support To be perfect
Check whether the search is normal and the list is displayed properly support To be perfect
Check whether the local loading time is within 1s support

Problems and functional limitations

  1. Get the components in the page onlypage.$()orpage.? (a)Method, the selector supports only component names and class names. Unable to get component elements inside a custom component directly, you need to prefix the class name. The page of the actual project uses a lot of custom components, which is very inconvenient to judge the internal structure of the custom componentswxml()Method prints out the internal structure of the custom component to verify the actual condition of the internal child components. And you cannot call methods inside a custom component.
  2. Jest’s Snapshot feature is a great way to test components or pages with relatively fixed structures, but it can be tricky to use. The comparison content of snapshot in the mini-program is usually a string printed by the WXML method of the component. However, at runtime, the WXML method may return different results. The component may be automatically added with the WX: nodeID attribute, but sometimes the returned string is not added, causing the Snapshot test to fail.
  3. Currently, it can only be tested in the developer tool environment, so the live broadcast function cannot be tested, and cloud VOD will be automatically downgraded to Tencent video on Demand, and live broadcast cannot be tested either. (The tool is updated to support real machine debugging, which should be improved)
  4. Login, scan and other functions cannot be tested because automation tools cannot scan and click authorization pop-ups.
  5. <web-view>Components have no access to any internal information and cannot be automated.

Hope these problems can be solved later ~~