Maison > Article > Applet WeChat > Explication détaillée des tests automatisés de petits programmes
[Recommandations d'apprentissage associées : Tutoriel du mini programme WeChat]
Récemment, l'équipe prévoit de faire un test automatisé d'un mini-programme L'outil devrait permettre au personnel de l'entreprise de restaurer automatiquement le chemin d'opération précédent après l'exécution du mini-programme et de capturer les exceptions qui se produisent pendant l'opération pour déterminer si cette version affectera les fonctions de base du mini-programme.
La description ci-dessus semble simple, mais il y a encore quelques difficultés au milieu. La première difficulté est de savoir comment enregistrer les opérations lorsque le personnel de l'entreprise opère. le mini programme Path, la deuxième difficulté est de savoir comment restaurer le chemin d'opération enregistré.
Comment restaurer le chemin d'opération pour ce problème, le SDK officiel est préféré : miniprogram-automator
.
Le SDK Mini Program Automation fournit aux développeurs un ensemble de solutions pour contrôler les mini-programmes via des scripts externes, atteignant ainsi l'objectif de test automatisé des mini-programmes. Grâce à ce SDK, vous pouvez effectuer les opérations suivantes :
Les descriptions ci-dessus proviennent toutes de documents officiels. Il est recommandé de lire les documents officiels avant de lire le contenu suivant. Bien sûr, si vous avez déjà utilisé Puppeteer, vous pouvez également démarrer rapidement. fondamentalement la même chose. Ce qui suit est une brève introduction à la façon d’utiliser le SDK.
// 引入sdkconst automator = require('miniprogram-automator')// 启动微信开发者工具automator.launch({ // 微信开发者工具安装路径下的 cli 工具 // Windows下为安装路径下的 cli.bat // MacOS下为安装路径下的 cli cliPath: 'path/to/cli', // 项目地址,即要运行的小程序的路径 projectPath: 'path/to/project', }).then(async miniProgram => { // miniProgram 为 IDE 启动后的实例 // 启动小程序里的 index 页面 const page = await miniProgram.reLaunch('/page/index/index') // 等待 500 ms await page.waitFor(500) // 获取页面元素 const element = await page.$('.main-btn') // 点击元素 await element.tap() // 关闭 IDE await miniProgram.close() })复制代码
Un rappel : avant d'utiliser le SDK, vous devez ouvrir le port de service des outils de développement, sinon le démarrage échouera.
Avec la méthode de restauration du chemin d'opération, l'étape suivante consiste à résoudre le problème de l'enregistrement du chemin d'opération.
Dans le mini programme, il n'est pas possible de capturer tous les événements dans la fenêtre via le bouillonnement d'événements comme dans le web. Heureusement, toutes les pages et composants du mini programme doivent passer les Page
et Component
. méthodes. à envelopper, afin que nous puissions réécrire ces deux méthodes, intercepter la méthode entrante et déterminer si le premier paramètre est un objet event
pour capturer tous les événements.
// 暂存原生方法const originPage = Pageconst originComponent = Component// 改写 PagePage = (params) => { const names = Object.keys(params) for (const name of names) { // 进行方法拦截 if (typeof obj[name] === 'function') { params[name] = hookMethod(name, params[name], false) } } originPage(params) }// 改写 ComponentComponent = (params) => { if (params.methods) { const { methods } = params const names = Object.keys(methods) for (const name of names) { // 进行方法拦截 if (typeof methods[name] === 'function') { methods[name] = hookMethod(name, methods[name], true) } } } originComponent(params) }const hookMethod = (name, method, isComponent) => { return function(...args) { const [evt] = args // 取出第一个参数 // 判断是否为 event 对象 if (evt && evt.target && evt.type) { // 记录用户行为 } return method.apply(this, args) } }复制代码
Le code ici ne fait que proxy toutes les méthodes d'événement et ne peut pas être utilisé pour restaurer le comportement de l'utilisateur. Pour restaurer le comportement de l'utilisateur, vous devez également savoir si le type d'événement est requis, tel qu'un clic, un appui long et. saisir.
const evtTypes = [ 'tap', // 点击 'input', // 输入 'confirm', // 回车 'longpress' // 长按]const hookMethod = (name, method) => { return function(...args) { const [evt] = args // 取出第一个参数 // 判断是否为 event 对象 if ( evt && evt.target && evt.type && evtTypes.includes(evt.type) // 判断事件类型 ) { // 记录用户行为 } return method.apply(this, args) } }复制代码
Après avoir déterminé le type d'événement, vous devez toujours clarifier sur quel élément vous avez cliqué. Cependant, le piège dans le mini-programme est qu'il n'y a pas de nom de classe de l'élément dans l'attribut cible de l'objet événement, mais l'élément peut être obtenu.
Afin d'obtenir les éléments avec précision, il faut ajouter une étape dans la construction, modifier le fichier wxml et copier le class
attributs de tous les éléments Servir dans data-className
.
<!-- 构建前 --><view></view><view></view><!-- 构建后 --><view></view><view></view>复制代码
Mais après avoir obtenu la classe, il y aura un autre écueil. L'outil de test automatisé du mini programme ne peut pas obtenir directement les éléments du composant personnalisé sur la page. Le composant personnalisé doit d'abord être obtenu.
<!-- Page --><toast></toast><!-- Component --><view> <text>{{text}}</text> <view></view></view>复制代码
// 如果直接查找 .toast-close 会得到 nullconst element = await page.$('.toast-close') element.tap() // Error!// 必须先通过自定义组件的 tagName 找到自定义组件// 再从自定义组件中通过 className 查找对应元素const element = await page.$('toast .toast-close') element.tap()复制代码
Ainsi, lorsque nous construisons l'opération, nous devons également insérer tagName pour l'élément.
<!-- 构建前 --><view></view><toast></toast><!-- 构建后 --><view></view><toast></toast>复制代码
Nous pouvons désormais continuer à enregistrer joyeusement le comportement des utilisateurs.
// 记录用户行为的数组const actions = [];// 添加用户行为const addAction = (type, query, value = '') => { actions.push({ time: Date.now(), type, query, value }) }// 代理事件方法const hookMethod = (name, method, isComponent) => { return function(...args) { const [evt] = args // 取出第一个参数 // 判断是否为 event 对象 if ( evt && evt.target && evt.type && evtTypes.includes(evt.type) // 判断事件类型 ) { const { type, target, detail } = evt const { id, dataset = {} } = target const { className = '' } = dataset const { value = '' } = detail // input事件触发时,输入框的值 // 记录用户行为 let query = '' if (isComponent) { // 如果是组件内的方法,需要获取当前组件的 tagName query = `${this.dataset.tagName} ` } if (id) { // id 存在,则直接通过 id 查找元素 query += id } else { // id 不存在,才通过 className 查找元素 query += className } addAction(type, query, value) } return method.apply(this, args) } }复制代码
Jusqu'à présent, tous les clics, saisies et opérations liées à la saisie de l'utilisateur ont été enregistrés. Il reste cependant une opération de défilement de l'écran qui n'est pas enregistrée. On peut directement déléguer la méthode onPageScroll
de Page.
// 记录用户行为的数组const actions = [];// 添加用户行为const addAction = (type, query, value = '') => { if (type === 'scroll' || type === 'input') { // 如果上一次行为也是滚动或输入,则重置 value 即可 const last = this.actions[this.actions.length - 1] if (last && last.type === type) { last.value = value last.time = Date.now() return } } actions.push({ time: Date.now(), type, query, value }) } Page = (params) => { const names = Object.keys(params) for (const name of names) { // 进行方法拦截 if (typeof obj[name] === 'function') { params[name] = hookMethod(name, params[name], false) } } const { onPageScroll } = params // 拦截滚动事件 params.onPageScroll = function (...args) { const [evt] = args const { scrollTop } = evt addAction('scroll', '', scrollTop) onPageScroll.apply(this, args) } originPage(params) }复制代码
Il y a un point d'optimisation ici, c'est-à-dire que lors de l'enregistrement de l'opération de défilement, vous pouvez juger si la dernière opération était également une opération de défilement. S'il s'agit de la même opération, il vous suffit de modifier le défilement. distance, car les deux Le deuxième défilement peut être effectué en une seule étape. De la même manière, il en va de même pour les événements d'entrée, et la valeur d'entrée peut également être atteinte en une seule étape.
Une fois l'opération utilisateur terminée, le texte json du comportement de l'utilisateur peut être affiché dans la console. Après avoir copié le texte json, il peut être exécuté via l'outil d'automatisation. .
// 引入sdkconst automator = require('miniprogram-automator')// 用户操作行为const actions = [ { type: 'tap', query: 'goods .title', value: '', time: 1596965650000 }, { type: 'scroll', query: '', value: 560, time: 1596965710680 }, { type: 'tap', query: 'gotoTop', value: '', time: 1596965770000 } ]// 启动微信开发者工具automator.launch({ projectPath: 'path/to/project', }).then(async miniProgram => { let page = await miniProgram.reLaunch('/page/index/index') let prevTime for (const action of actions) { const { type, query, value, time } = action if (prevTime) { // 计算两次操作之间的等待时间 await page.waitFor(time - prevTime) } // 重置上次操作时间 prevTime = time // 获取当前页面实例 page = await miniProgram.currentPage() switch (type) { case 'tap': const element = await page.$(query) await element.tap() break; case 'input': const element = await page.$(query) await element.input(value) break; case 'confirm': const element = await page.$(query) await element.trigger('confirm', { value }); break; case 'scroll': await miniProgram.pageScrollTo(value) break; } // 每次操作结束后,等待 5s,防止页面跳转过程中,后面的操作找不到页面 await page.waitFor(5000) } // 关闭 IDE await miniProgram.close() })复制代码
Il s'agit simplement d'une simple restauration du comportement de fonctionnement de l'utilisateur. Pendant le fonctionnement réel, les requêtes réseau et les simulations de stockage local seront également impliquées, qui ne seront pas décrites ici. En parallèle, nous pouvons également accéder à l’outil jest pour faciliter la rédaction de cas d’utilisation.
Pour des besoins apparemment difficiles, à condition de les explorer attentivement, vous pouvez toujours trouver une solution correspondante. De plus, il existe de nombreux pièges dans les outils d'automatisation des mini-programmes WeChat. Si vous rencontrez des problèmes, vous pouvez d'abord vous rendre dans la communauté des mini-programmes pour les trouver. La plupart des pièges ont été surmontés par les prédécesseurs. des problèmes qui ne peuvent pas être résolus pour le moment et que vous ne pouvez trouver que d’autres moyens de les éviter. Enfin, je souhaite que le monde soit sans bugs.
Recommandations d'apprentissage associées : Tutoriel de développement de compte public WeChat
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!