> 위챗 애플릿 > 미니 프로그램 개발 > 소규모 프로그램 자동화 테스트에 대한 자세한 설명

소규모 프로그램 자동화 테스트에 대한 자세한 설명

coldplay.xixi
풀어 주다: 2020-08-21 17:11:47
앞으로
3381명이 탐색했습니다.

소규모 프로그램 자동화 테스트에 대한 자세한 설명

[관련 학습 권장 사항: WeChat 미니 프로그램 튜토리얼]

Background

최근 팀에서는 미니 프로그램을 자동으로 테스트하는 도구를 만들 계획이며, 이를 통해 비즈니스 담당자가 운영 후 이전 작업을 자동으로 복원할 수 있기를 바랍니다. 미니 프로그램 경로 및 작업 중에 발생하는 예외를 캡처하여 이번 릴리스가 미니 프로그램의 기본 기능에 영향을 미치는지 여부를 확인합니다.

소규모 프로그램 자동화 테스트에 대한 자세한 설명

위의 설명은 간단해 보이지만 여전히 중간에 난관이 좀 있습니다. 첫 번째 난관은 사업 담당자가 미니 프로그램을 운영할 때 작업 경로를 기록하는 방법이고, 두 번째 난관은 휴대하는 방법입니다. 기록된 작업 경로를 축소합니다.

Automation SDK

작업 경로를 복원하는 방법은 무엇입니까? 이 문제의 경우 공식 SDK인 miniprogram-automator를 사용하는 것이 좋습니다. miniprogram-automator

小程序自动化 SDK 为开发者提供了一套通过外部脚本操控小程序的소규모 프로그램 자동화 테스트에 대한 자세한 설명,从而实现小程序自动化测试的目的。通过该 SDK,你可以做到以下事情:

  • 控制小程序跳转到指定页面
  • 获取小程序页面数据
  • 获取小程序页面元素状态
  • 触发小程序元素绑定事件
  • 往 AppService 注入代码片段
  • 调用 wx 对象上任意接口
  • ...

上面的描述都来自官方文档,建议阅读后面内容之前可以先看看官方文档,当然如果之前用过 puppeteer ,也可以快速上手,api 基本一致。下面简单介绍下 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()
})复制代码
로그인 후 복사

有个地方需要提醒一下:使用 SDK 之前需要开启开发者工具的服务端口,要不然会启动失败。

소규모 프로그램 자동화 테스트에 대한 자세한 설명

捕获用户行为

有了还原操作路径的办法,接下来就要解决记录操作路径的难题了。

在小程序中,并不能像 web 中通过事件冒泡的方式在 window 中捕获所有的事件,好在小程序所以的页面和组件都必须通过 PageComponent 方法来包装,所以我们可以改写这两个方法,拦截传入的方法,并判断第一个参数是否为 event 对象,以此来捕获所有的事件。

// 暂存原生方法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)
  }
}复制代码
로그인 후 복사

这里的代码只是代理了所有的事件方法,并不能用来还原用户的行为,要还原用户行为还必须知道该事件类型是否是需要的,比如点击、长按、输入。

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)
  }
}复制代码
로그인 후 복사

确定事件类型之后,还需要明确点击的元素到底是哪个,但是小程序里面比较坑的地方就是,event 对象的 target 属性中,并没有元素的类名,但是可以获取元素的 dataset。

소규모 프로그램 자동화 테스트에 대한 자세한 설명

为了准确的获取元素,我们需要在构建中增加一个步骤,修改 wxml 文件,将所有元素的 class 属性复制一份到 data-className 中。

<!-- 构建前 --><view></view><view></view><!-- 构建后 --><view></view><view></view>复制代码
로그인 후 복사

但是获取到 class 之后,又会有另一个坑,小程序的自动化测试工具并不能直接获取页面里自定义组件中的元素,必须先获取自定义组件。

<!-- 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()复制代码
로그인 후 복사

所以我们在构建操作的时候,还需要为元素插入 tagName。

<!-- 构建前 --><view></view><toast></toast><!-- 构建后 --><view></view><toast></toast>复制代码
로그인 후 복사

现在我们可以继续愉快的记录用户行为了。

// 记录用户行为的数组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)
  }
}复制代码
로그인 후 복사

到这里已经记录了用户所有的点击、输入、回车相关的操作。但是还有滚动屏幕的操作没有记录,我们可以直接代理 Page 的 onPageScroll

미니 프로그램 자동화 SDK는 개발자에게 외부 스크립트를 통해 미니 프로그램을 제어할 수 있는 솔루션 세트를 제공하여 미니 프로그램의 자동화된 테스트 목적을 달성합니다. 이 SDK를 통해 다음을 수행할 수 있습니다:

  • 미니 프로그램을 제어하여 지정된 페이지로 이동
  • 미니 프로그램 페이지 데이터 가져오기
  • 미니 가져오기 프로그램 페이지 요소 상태
  • 미니 프로그램 요소 바인딩 이벤트 트리거
  • AppService에 코드 조각 삽입
  • wx 개체에서 인터페이스 호출
  • ...
위 설명은 모두 공식 문서에서 발췌한 것입니다. 물론, 이전에 Puppeteer를 사용해 본 적이 있는 경우에도 시작하실 수 있습니다. API는 기본적으로 동일합니다. 다음은 SDK 사용방법을 간략하게 소개합니다.

// 记录用户行为的数组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)
}复制代码
로그인 후 복사

한 가지 주의할 점이 있습니다. SDK를 사용하기 전에 개발자 도구의 서비스 포트를 열어야 합니다. 그렇지 않으면 시작이 실패합니다.

서비스 포트 열기

🎜

사용자 행동 캡처🎜🎜작업 경로 복원 방법으로 다음 단계는 녹음 문제를 해결하는 것입니다. 작업 경로. 🎜🎜미니 프로그램에서는 웹처럼 이벤트 버블링을 통해 창에 있는 모든 이벤트를 캡처할 수는 없습니다. 다행히 미니 프로그램의 모든 페이지와 구성 요소는 페이지, 를 통과해야 합니다. 래핑할 구성 요소 메서드를 사용하여 이 두 메서드를 다시 작성하고 들어오는 메서드를 가로채고 첫 번째 매개변수가 모든 이벤트를 캡처하는 event 개체인지 확인합니다. 🎜
// 引入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()
})复制代码
로그인 후 복사
🎜여기의 코드는 모든 이벤트 메소드만을 나타내며 사용자의 행동을 복원하는 데 사용할 수 없습니다. 사용자의 행동을 복원하려면 클릭, 길게 누르기, 입력 등 이벤트 유형이 필요한지 여부도 알아야 합니다. 🎜rrreee🎜이벤트 유형을 결정한 후에도 어떤 요소가 클릭되었는지 명확히 해야 합니다. 그러나 미니 프로그램의 함정은 이벤트 개체의 대상 속성에 해당 요소의 클래스 이름이 없지만 해당 요소의 데이터 세트가 있다는 것입니다. 요소를 얻을 수 있습니다. 🎜🎜event object🎜🎜🎜🎜요소를 정확하게 얻으려면 구성 단계를 추가하고 wxml 파일을 수정한 다음 class 속성을 ​​복사해야 합니다. data-className의 모든 요소. 🎜rrreee🎜하지만 클래스를 얻은 후에는 또 다른 함정이 있습니다. 미니 프로그램의 자동화된 테스트 도구는 페이지의 사용자 정의 구성 요소를 직접 얻을 수 없습니다. 먼저 사용자 정의 구성 요소를 얻어야 합니다. 🎜rrreeerrreee🎜따라서 작업을 빌드할 때 요소에 tagName도 삽입해야 합니다. 🎜rrreee🎜이제 사용자 행동을 계속해서 즐겁게 기록할 수 있습니다. 🎜rrreee🎜지금까지 사용자의 클릭, 입력, 엔터 관련 작업이 모두 기록되었습니다. 그러나 스크롤 화면 작업은 기록되지 않습니다. Page의 onPageScroll 메서드를 직접 프록시할 수 있습니다. 🎜rrreee🎜 여기에 최적화 포인트가 있습니다. 즉, 스크롤 작업을 기록할 때 마지막 작업도 스크롤 작업인지 판단할 수 있습니다. 동일한 작업이라면 스크롤 거리만 수정하면 되기 때문입니다. 두 개의 스크롤을 한 번에 수행할 수 있습니다. 입력 이벤트도 마찬가지이며, 입력값도 한 단계로 도달할 수 있습니다. 🎜🎜사용자 행동 복원🎜🎜사용자 작업이 완료된 후 사용자 행동의 json 텍스트를 콘솔에 출력할 수 있습니다. json 텍스트를 복사한 후 자동화 도구를 통해 실행할 수 있습니다. 🎜rrreee🎜이것은 사용자의 작업 동작을 간단히 복원하는 것일 뿐입니다. 실제 작업 중에는 네트워크 요청 및 로컬 저장소 모의 작업도 포함되므로 여기서는 설명하지 않습니다. 동시에 Jest 도구에 액세스하여 사용 사례를 더 쉽게 작성할 수도 있습니다. 🎜

요약

어려워 보이는 요구 사항에 대해 주의 깊게 탐색하는 한 언제든지 해당 솔루션을 찾을 수 있습니다. 또한 WeChat 미니 프로그램의 자동화 도구에는 정말 많은 함정이 있습니다. 문제가 발생하면 먼저 미니 프로그램 커뮤니티에 가서 대부분의 함정을 찾을 수 있습니다. 현재 해결할 수 없는 문제는 피할 수 있는 다른 방법만 찾을 수 있습니다. 마지막으로, 버그 없는 세상이 되었으면 좋겠습니다.

관련 학습 권장사항: WeChat 공개 계정 개발 튜토리얼

위 내용은 소규모 프로그램 자동화 테스트에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:juejin.im
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿