Skip to content

📱 零代码快准稳 UI 智能录制回放平台 🚀 3 像素内自动精准定位,2 毫秒内自动精准等待,用户包含腾讯,应微信团队邀请分享了 零代码测试工具与实践(API•单元•UI) 📱 Coding-free, fast, accurate and stable UI replayer 🚀 Incredible ±3px auto locating and ±2ms auto waiting. Used by Tencent, invited by WeChat team to share

License

Notifications You must be signed in to change notification settings

TommyLemon/UIGO

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

UIGO

📱 零代码快准稳 UI 智能录制回放平台 🚀

3 像素内自动精准定位,2 毫秒内自动精准等待,录制回放快、准、稳!

English 录制回放 快速上手 在线工具 AI 问答



UIGO - 📱 零代码快准稳 UI 智能录制回放平台 🚀

3 像素内自动精准定位,2 毫秒内自动精准等待,录制回放快、准、稳!
适用于 一次录制到处回放、反复回归界面操作、App UI/功能 自动化测试、
帮助开发快速复现和排查 bug、方便判断 bug 原因出在前端还是后端 等,
大量减少耗时费力又无聊的重复手工操作,大幅提高手工和自动化测试效率,
强力杜绝 测试和开发、前端和后端 关于缺陷单踢皮球等各种低效扯皮内耗!

用户包含腾讯,应微信团队邀请分享了 零代码测试工具与实践(API•单元•UI)

支持功能

  • 零代码 录制和回放 触屏、按键、键盘、数据 等
  • 支持 16:9 标准屏、19.5:9 全面屏等各种设备屏幕
  • 支持原生页面、内置 H5 网页、浏览器加载网页等
  • 支持 Android 真机、Studio/Genymotion 等模拟器
  • 单双指点击、长按、滑动、缩放各种像素级精细操作
  • 自动精准等待、模拟 HTTP API 的请求和响应数据
  • 不同机型录制回放偏差基本仅在 3 像素、2 毫秒 内
  • 可从任意界面开始和停止录制、回放,绕过登录问题
  • 可自动对关键步骤截屏,方便对比回放与录制差异
  • 可自动和手动选择 View 及触摸区域、贴靠方式等
  • 可保存录制步骤相关数据到后端数据库及从后端下载
  • 可用管理端网页浏览检索用例和远程控制录制回放
  • 附带 UnitAuto-机器学习零代码自动化单元测试
  • 中文和英语双语文案,根据系统语言设置自动切换

特点优势

相比各种 UI 录制回放/自动化测试 的 其它平台/工具/框架:

1.它们录制过程各种别扭难用反人类,甚至还需要开发/维护用例脚本、每个用例都写一大堆代码频繁部署等;
UIGO 不需要写任何代码,录制几乎是按和人正常操作完全一样的方式,操作简单易用,录制回放快、准、稳!

2.它们很难兼容各种不同宽高比分辨率屏幕,720P, 1080P 等 16:9 屏幕录制最多只能较好地在 16:9 屏幕回放,
即便手写代码或图像比对等也很难在列表项 View id/图标 重复控件精准定位,经常点错位置导致大量回放失败;
UIGO 则能很好地支持 16:9, 19.5:9 等各种不同屏幕录制,然后在 720P, 1080P, 2K, 1080X2340, 1440X3200
等各种 不同机型、不同系统、不同屏幕 基本都能很好地精准回放,偏差基本仅在相当于一根头发丝的 3 像素内!


3.它们要到处人为设置/调整操作步骤等待时间,还总是要么等太久、要么还没返回就过早执行下一步导致出错,
因为几乎无法保证网络请求在精准时间内返回,所以总是界面没加载完就滑动、弹窗没显示就点了"确定"位置等;
UIGO 则会自动精准等待 App 发送的各种 HTTP API 网络请求,偏差基本在 2 毫秒内,比眨眼一次还要快 50 倍,
像专业的测试工程师一样精准高效地等待数据和 UI 都加载好并执行 点击、长按、滑动、缩放 等每一步对应操作!


原理说明

被测项目不需要写任何用例脚本代码(逻辑代码、注解代码、配置代码等全都不要),
UIGO 会自动录制 UI 触屏操作、虚拟+实体按键操作、HTTP API 网络请求与响应、
Activity, Fragment, Dialog, PopupWindow 等各种组件(控件)元素的生命周期 等,
回放时根据录制触摸点所在被分割球划分的 上、下、左、右、居中、等比 等区域
以及 屏幕分辨率、状态栏高度、导航栏高度、键盘高度 等来自动计算出回放触摸点,
再加上 id(如果有) 相同且距离最近的 View 区域来辅助微调,高度精准回放触屏操作!
对 返回按键、键盘按键 甚至 输入框编辑过程的每个变化的字符 也都能精准无误地还原!


示例项目

UIGO Android 简单测试 App 直接 下载 (第一次可能失败,返回报错 JSON,一般重试一次就可以)
UIGO Android 复杂客户端 App 直接 下载 (第一次可能失败,返回报错 JSON,一般重试一次就可以)

安装 App 必须授权 显示悬浮窗、读写文件存储 这两个权限

其它申请的权限也尽可能都勾选授权,如果不能提前授权,则在使用时弹出是否申请权限弹窗后再确认授权
TommyLemon/APIAuto#61 (comment)


早期零代码单机录制不同分辨率双机同时回放视频

https://www.bilibili.com/video/BV1CK4218788

早期管理端网页工具零代码远程控制手机录制回放视频

https://www.bilibili.com/video/BV1wA4m137ha

早期仿微信朋友圈复杂 App 录制回放,弹窗、输入、网页、滑动、点击等

https://www.bilibili.com/video/BV1fH4y1E7gD

零代码录制回放 H5 移动端网页输入、滑动、点击等操作

https://www.bilibili.com/video/BV1TK421C7y4

录制用例

1.按业务 App 提供的方式打开 UIAuto 管理首页,例如 APIJSONApp 是登录后点击首页标题,UIAuto-Android 是点击首页 [自动 UI 测试] 按钮

2.点击 Record 录制按钮 > 点击顶部悬浮长条中间的 Record 录制 按钮开始录制 > 正常操作 App > 完成一个用例过程后,点击半透明圆形 〇 悬浮球完成录制

3.(可选)点击右下角 post 按钮上传录制的操作和数据等到后端数据库,可先编辑底部的后端服务器 HTTP URL Host 地址为你自己部署的 APIJSON 后端服务

录制注意事项

1.按贴靠方式调整分割球位置及贴靠方式
按下(MotionEvent.ACTION_DOWN 事件)对应 View 的屏幕 [X, Y] 坐标位置前,必须先完成这步(如果已经配置正确则跳过)

分割球对应横纵两条分割线把屏幕分成了 左上、右上、左下、右下 4 个区域,对应数学上按坐标轴划分的 第 二、一、三、四 象限;
点击顶部控制条左上角 # 按钮可切换单双分割球,相比单分割球可更精细地设置高级属性

双分割球中间会出现一个长方形触控区域,默认点击位置按居中处理,可点击切换 center 居中, ratio 等比, top 靠上, bottom 靠下, left 靠左, right 靠右;
分割球本身贴靠方式可以点击分割球来切换 top_left 左上, top_right 右上, bottom_left 左下, bottom_right 右下, ratio 等比,
ratio_top 靠上并垂直纵向等比, ratio_bottom 靠下并垂直纵向等比, ratio_left 靠左并水平横向等比, ratio_right 靠右并水平横向等比

2.滑动要该快则快、该慢则慢
分页列表/网格等滑动尽量快速到底,保证不同宽高比分辨率屏幕上都能在同侧(↓ 从上往下、↑ 从下往上、→ 从左往右、← 从右往左)上显示一致;
点按内部 View 前可缓慢滑动保证目标出现在屏幕内,且手离开屏幕前先在同一位置稳住别动,保证不继续自动滚动(不同机型/系统对惯性滚动处理不一致导致偏差)

3.触控的 View 尽量都有 id,且尽量不要改动
UIGO 会对有 id 的被触控 View 在回放时进行微调触控位置(不改变 UI 布局,仅微调触屏输入坐标位置),灵活适配不同屏幕尤其是不同品牌机型的 UI 差异

4.最好开启托管服务器代理来录制 HTTP API 接口请求与响应流量,用于回放操作时同时回放数据
保证回放到某个步骤和录制到同一步骤时布局一致,不会触控点按错位

5.不要跳出 App,例如跳转外部浏览器或微信、支付宝等其它 App 来上网、登录、分享、支付等
目前不支持跨 App 录制回放,当前 App 外的对应事件录制不到也回放不了,打算后续支持对接支持跨 App 录制回放/自动化测试 的项目,
目前碰到这个问题要么避开,要么等待或手动点 》按钮跳过等待来直接继续执行下一步


回放用例

1.参考 录制用例 1 来打开 UIAuto 管理首页 > 点击左下角 Remote 共享列表 按钮 > 点击打开其中一个和 App 及账号对应的用例,或者 录制用例 后直接进入用例详情界面

2.点击用例详情界面左下角 Replay 回放按钮,返回到用例对应开始录制的业务 App 界面,保证状态一致(都是刚进入后没有操作的界面,或者分页列表都下拉刷新过等)

3.点击顶部悬浮长条中间的 Replay 回放 按钮开始回放,观察每步操作前后,App 的 UI 展示、界面数据、界面跳转、弹窗显示、键盘输入等是否符合预期(和录制时表现一样)

回放注意事项

1.回放开始时 App 初始状态必须和录制时一致
包括所在页面(最好都在首页)、列表/网格滚动距离(最好都是没滚动的初始值 0)、中英文等语言设置、字体大小和样式设置、缓存/调试等自定义配置 等

2.系统的中英文等语言、字体大小和样式 等设置尽可能保持一致

3.最好开启托管服务器代理来回放录制时的 HTTP API 接口请求与响应流量数据


快速上手

可先跳过这个步骤,先下载体验 App 安装包,安装后 按以下 录制用例、回放用例 文档来操作

集成到被测项目 Android 客户端 App

1.依赖 UnitAuto-Apk

UnitAuto-Apk 导入到你项目 app moudule 所在目录settings.gradle

include ':UnitAuto-Apk'

app moudule 目录build.gradle

dependencies {
    api project(':UnitAuto-Apk')
}

2.依赖 UIAuto

UIAuto 导入到你项目 app moudule 所在目录settings.gradle

include ':UIAuto'

app moudule 目录build.gradle

dependencies {
    api project(':UIAuto')
}

2.初始化 UIAuto

Application onCreate 方法 中初始化

    @Override
    public void onCreate() {
        super.onCreate();
        UIAutoApp.STEP_TIMEOUT = 30*1000; // 一般为 HTTP API 网络请求超时时间 ms 毫秒值
        UIAutoApp.getInstance().initUIAuto(this);
    }

3.提供 UIAuto 管理界面入口

AndroidManifest.xml 中注册 UIAutoActivity

<manifest ... >
    <application ... >
      
        <activity
            android:name="uiauto.UIAutoActivity"
            android:label="@string/ui"
            android:windowSoftInputMode="adjustPan"
            android:configChanges="orientation|screenSize"
            android:screenOrientation="portrait"
            />
        <activity
            android:name="uiauto.UIAutoListActivity"
            android:label="@string/ui"
            android:windowSoftInputMode="adjustPan"
            android:screenOrientation="portrait"
            />

        <activity
            android:name="unitauto.apk.UnitAutoActivity"
            android:label="@string/unit"
            android:windowSoftInputMode="adjustPan"
            android:configChanges="orientation|screenSize"
            android:screenOrientation="userLandscape"
            />
      
     </application>
</manifest>

可在你项目的任何界面新增一个按钮或其它形式的入口,仅 DEBUG 模式下展示

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClickUI"
        android:text="UIAutoActivity"
        android:textAllCaps="false"
        />

参考 layout/main_tab_activity

点击这个入口跳转到 UIAutoActivity

    public void onClickUI(View v) {
        startActivity(UIAutoActivity.createIntent(this));
    }

参考 MainTabActivity

4.通知 HTTP API 请求与相应

在 HTTP API 发起请求处加上

		UIAutoApp.getInstance().post(new Runnable() {
			@Override
			public void run() {
				UIAutoApp.getInstance().onHTTPEvent(
					action, type, method, host, url, headerStr, reqBodyStr, null, activity, fragment
				);
			}
		});

参考:
https://github.com/TommyLemon/UIGO/blob/master/APIJSONApp/app/src/main/java/apijson/demo/manager/HttpManager.java#L137-L147

在 HTTP API 响应结果处加上

		UIAutoApp.getInstance().post(new Runnable() {
			@Override
			public void run() {
				UIAutoApp.getInstance().onHTTPEvent(
					- action, responseCode, method, host, url, headerStr, reqBodyStr, resBodyStr, null, activity, fragment
				);
			}
		});

参考:
https://github.com/TommyLemon/UIGO/blob/master/APIJSONApp/app/src/main/java/apijson/demo/manager/HttpManager.java#L168-L179


5.通知 Dialog, PopupWindow 显示和隐藏

业务代码中如果使用了 android.app.AlertDialog,且录制回放用例涉及,则可以换成 uiauto.AlertDialog,
最简单的方式是顶部菜单 Edit > Find > Replace in files 全局搜索 import android.app.AlertDialog,
然后点搜索弹窗右下角 Replace All 按钮批量改为 import uiauto.AlertDialog。
如果因为用了自定义或第三方通用 Base Alert Dialog 不方便替换,则可以 extends uiauto.AlertDialog 或在里面加上:

	@Override
	public void show() {
		super.show();
		UIAutoApp.getInstance().onUIAutoDialogShow(this); // 通知已显示
	}

	// 拦截隐藏事件监听
	private OnDismissListener listener;
	@Override
	public void setOnDismissListener(OnDismissListener listener) {
		this.listener = listener;
	}

	private Activity context;
	private void init(Context ctx) {
		this.context = (Activity) ctx;

		super.setOnDismissListener(new OnDismissListener() {
			@Override
			public void onDismiss(DialogInterface dialog) {
				if (listener != null) {
					listener.onDismiss(dialog);
				}

				UIAutoApp.getInstance().onUIAutoDialogDismiss(Dialog.this); // 通知已隐藏
			}
		});
	}

	// 每个可重写的构造方法都保证调用到 init 方法
	public AlertDialog(Context context) {
		super(context);
		init(context);
	}

	public AlertDialog(Context context, @StyleRes int themeResId) {
		super(context, themeResId);
		init(context);
	}

	public AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
		super(context, cancelable, cancelListener);
		init(context);
	}

所有 Dialog, DatePickerDialog, TimePickerDialog, ProgressDialog, CharacterPickerDialog 等也同上处理。


业务代码中如果使用了 android.widget.PopupWindow,且录制回放用例涉及,则可以换成 uiauto.PopupWindow,
最简单的方式是顶部菜单 Edit > Find > Replace in files 全局搜索 import android.widget.PopupWindow,
然后点搜索弹窗右下角 Replace All 按钮批量改为 import uiauto.PopupWindow。
如果因为用了自定义或第三方通用 Base Popup Window 不方便替换,则可以 extends uiauto.PopupWindow 或在里面加上:

    private android.widget.PopupWindow popupWindow;
    private View view;

    @Override
    public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
        super.showAsDropDown(anchor, xoff, yoff, gravity);

        if (view == null) {
            try {
                Field field = android.widget.ListPopupWindow.class.getDeclaredField("mPopup");
                field.setAccessible(true);
                popupWindow = (android.widget.PopupWindow) field.get(this);
                //  popupWindow.setOutsideTouchable(false);

                Field dvField = android.widget.PopupWindow.class.getDeclaredField("mDecorView");
                dvField.setAccessible(true);
                view = (View) dvField.get(popupWindow);

                if (view == null) {
                    View cv = popupWindow.getContentView();
                    view = cv == null ? null : cv.getRootView();
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }

        if (view == null) {
            View cv = getContentView();
            view = cv == null ? null : cv.getRootView();
        }

        Window w = getWindow();

        // 通知已显示
        UIAutoApp app = UIAutoApp.getInstance();
        app.onUIAutoWindowCreate(w.getCallback(), w);
        app.setCurrentPopupWindow(popupWindow, view, null, context, null);
    }


    // 拦截隐藏事件监听
    private android.widget.PopupWindow.OnDismissListener listener;
    @Override
    public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener listener) {
        this.listener = listener;
    }

    public Window getWindow() {
        Activity ctx = view == null ? null : (Activity) view.getContext();
        if (ctx == null) {
            ctx = context;
        }
        return ctx.getWindow();
    }

    private Activity context;
    private void init(Context ctx) {
        this.context = (Activity) ctx;

        super.setOnDismissListener(new android.widget.PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {
                if (listener != null) {
                    listener.onDismiss();
                }

                Window w = getWindow();

                // 通知已隐藏
                UIAutoApp app = UIAutoApp.getInstance();
                app.onUIAutoWindowDestroy(w.getCallback(), w);
                app.setCurrentPopupWindow(null, null, null, context, null);
            }
        });
    }

    // 每个可重写的构造方法都保证调用到 init 方法
    public PopupWindow(Context context) {
        super(context);
        init(context);
    }

    public PopupWindow(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public PopupWindow(Context context, AttributeSet attrs, @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    public PopupWindow(Context context, AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    public PopupWindow(Context context, View contentView) {
        super(contentView);
        init(context);
    }

    public PopupWindow(Context context, int width, int height) {
        super(width, height);
        init(context);
    }

    public PopupWindow(Context context, View contentView, int width, int height) {
        super(contentView, width, height);
        init(context);
    }

    public PopupWindow(Context context, View contentView, int width, int height, boolean focusable) {
        super(contentView, width, height, focusable);
        init(context);
    }

所有 ListPopupWindow 等也同上处理。

Java 后端 Server

可先跳过,使用 http://apijson.cn:8080http://apijson.cn:9090 代替

见 APIJSON-Demo 后端上手
https://github.com/APIJSON/APIJSON-Demo?tab=readme-ov-file#1%E5%90%8E%E7%AB%AF%E4%B8%8A%E6%89%8B


录制、回放用例

见以上 录制用例回放用例 的说明。



常见问题

1.apijson.cn 访问不了

托管服务地址改为 http://47.98.196.224:8080
TommyLemon/APIAuto#13


更多常见问题
https://github.com/TommyLemon/APIAuto/issues


技术交流

关于作者

https://github.com/TommyLemon

如果有什么问题或建议可以 去 APIAuto 提 issue,交流技术,分享经验。
如果你解决了某些 bug,或者新增了一些功能,欢迎 提 PR 贡献代码,感激不尽。

其它项目

APIJSON 🚀 腾讯零代码、全功能、强安全 ORM 库 🏆 后端接口和文档零代码,前端(客户端) 定制返回 JSON 的数据和结构

APIAuto 敏捷开发最强大易用的 HTTP 接口工具,机器学习零代码测试、生成代码与静态检查、生成文档与光标悬浮注释,集 文档、测试、Mock、调试、管理 于一体的一站式体验

UnitAuto 机器学习零代码单元测试平台,零代码、全方位、自动化 测试 方法/函数 的正确性、可用性和性能

SQLAuto 智能零代码自动化测试 SQL 语句执行结果的数据库工具,任意增删改查、任意 SQL 模板变量、一键批量生成参数组合、快速构造大量测试数据

Android-ZBLibrary Android MVP 快速开发框架,Demo 全面,注释详细,使用简单,代码严谨

持续更新

https://github.com/TommyLemon/UIGO/commits/master

我要赞赏

UIGO 从私有仓库默默开发了 3 年多到现在终于开源了,Apache 证书对商用和非商用都很友好。
创作不易、坚持更难,右上角点亮 ⭐ Star 支持/收藏下本项目吧,谢谢 ^_^
https://github.com/TommyLemon/UIGO

About

📱 零代码快准稳 UI 智能录制回放平台 🚀 3 像素内自动精准定位,2 毫秒内自动精准等待,用户包含腾讯,应微信团队邀请分享了 零代码测试工具与实践(API•单元•UI) 📱 Coding-free, fast, accurate and stable UI replayer 🚀 Incredible ±3px auto locating and ±2ms auto waiting. Used by Tencent, invited by WeChat team to share

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy