• 这种情况我个人能想到的就还是多模板匹配了,个人比较擅长用图像识别的方式做 UI 自动化。

    1. 把不同座位的图截下来
    2. 遍历座位模板,多模板匹配,找出图中不同座位的分布信息
    3. 组合不同座位的分布信息,就可以得到上面我说的整体座位信息矩阵。分类的话就是按模板来的,匹配什么模板,就对应什么分类信息

    4. 涉及坐标变化,通常坐标变化都是有规律的,操作完之后记录整体的坐标变化量或者复位之后再继续操作也行。

    5. 可能还涉及不同设备的兼容性问题 (用图像识别都会遇到这个问题),先搞定上面的问题再看吧

    这个方案能解决问题,就是需要对图像处理和识别比较熟练。

  • 突然想到自绘的话也是从后端接口拿的数据,可以先请求后端座次信息接口,再计算对应坐标点,通过坐标的方式操作控件。

  • 问题的核心还是在于拿不到控件信息吧,用图像识别的多模板匹配是可以做到获取控件信息的。
    只要实现这样一个函数,就可以解决问题,
    输入:座位截图
    输出:座位信息矩阵
    [
    [Seat(), Seat(), Seat(), Seat(), Seat()],
    [Seat(), Seat(), Seat(), Seat(), Seat()],
    [Seat(), Seat(), Seat(), Seat(), Seat()],
    [Seat(), Seat(), Seat(), Seat(), Seat()],
    ]
    Seat 类里面包含了座位的是否可选、价格、类型等信息

  • 资源池的创建和销毁主要是靠框架维护,按任务隔离,每个任务都给一个 id,在任务执行完 (或执行异常) 时清掉该任务创建的资源。

    我的场景还不太一样,最初的痛点是经常会按不同的维度去划分服务,有时按系统分,有时按场景分,有时又按业务领域分,但是他们的一些基础依赖 (例如各个系统的登录、数据库连接等等) 又是会有重叠的,每个服务都自己创建一遍资源太浪费,每个资源都自己维持一个单例或者资源池又可能会受不同任务的并发影响,索性建立了一个按任务隔离的上下文 + 资源管理池。

    接口间这个数据传参我倒没有像楼主这样按 key、value 去划分,通常粒度控制在 DTO(对象,字典) 范围,我司的跨接口或跨系统传参经常都是按对象去传,比如一个订单要填地址信息,这个地址信息通常不是单个字段,而是包含姓名、电话、地址等多个字段的集合,我从获取地址信息的接口拿到这个集合,更改一下不同的 key 或者类型,多出来的不管,把它传递到下一个下单的接口。

  • 挺不错的实践,我最近在解决类似依赖问题的时候也是采用了同样的方案,用资源池解决上下游的耦合以及避免资源的重复创建,用依赖注入来减少主动去资源池拉资源的代码。
    主要的思想来源是 spring 和recoil

  • json 本质还是一个树结构呀,参考树的遍历,比较简单的就是使用递归或者栈。

  • 我见过的后端代码通常都比较简单粗暴,并不难懂,稍微熟悉一下 spring 框架,了解下分层思想和基础写法就可以了。时常感叹用 python 来写业务,代码少一半都不止。

    这个问题和是否掌握自己调试没关系,我前后端自己都能写,一样把 bug 修复交给开发去做,各个系统都有各种坑,这些坑开发都趟过,代码也是开发自己写的,设计细节,写法这些都是他本人最清楚,测试 debug 还要去理解每个开发的写法、思路、业务细节、实现细节,比开发自己调试慢很多。我通常只有在很明显能判断出大致错误原因时,才会给出自己大致的判断,然后走读代码确认是否和自己的判断一致。走读代码和 debug 程序只是为了提升自己,能提升效率时顺带提升效率,所有 bug 都自己去 debug,就谈不上效率了 。

  • 合理的,之前公司有这样做,研发自测用例其实和冒烟用例差不多的,主要作用在于保证提测时迭代主流程质量,不会因为主流程阻塞导致拖延测试进度。
    提供研发自测用例对于测试来说是好事,能提升提测质量,也给了测试人员卡提测准入的权利,最怕的反而是有规则却不遵守,开发一直忙于赶进度,不自测,联调敷衍了事,把问题留到测试阶段而且阻塞测试流程和进度。

  • 感觉测试类型还是跟着业务走的,之前在在线教育公司,就直播、报表、资讯、线索类挺多的。后来在货运公司,就音视频录制,地图、调度方面内容挺多的。

    感觉按大类分还是可行,比如报表、线索、活动、埋点都偏运营和数据方向。小程序、H5、APP 什么的属于用户端口

  • 没做过两年业务测试,但是居然全都接触测过。。不过感觉不应该这样分,太乱了。可以先分几个大类,再来细分小的

  • 很抱歉,以前用不上特征匹配,对 opencv 特征匹配这块的内容不熟,所以上面的回答有一些误导,今晚重新看了下文档,再回答一下。

    1. 关于我讲的取最大可信度的方法,其实用 flann.match(des1, des2) 就可以,match 方法是取最优结果,knnMatch 通过传入 k 值来取多个结果,我的做法就是用 match 就行了。
    2. 特征匹配这一块,airtest 算是实践相对较多的,去看了一下源码,有题主预期的代码实现,跳过去看就行了。 https://github.com/AirtestProject/Airtest/blob/master/airtest/aircv/sift.py
    1. 单独处理比较麻烦,可以把判断的部分做成参数,比如加个 side 参数,target = driver.sift_detect(source, side="left"),这样去调用就只判断屏幕左半部分的内容,右边的过滤掉,side 就由自己去设计了,上下左右,居中什么的都可以。

    2. 置信度就是相似度,你用来判断两个物体是否相似的阈值,查出来的结果是一个列表,通过相似度排序后取最大,这样虽然不 100% 准确,但是大多数情况下有效。

    1. 通过坐标点过滤,比如这个页面的 “同意” 按钮,坐标会更居中,靠下。
    2. 通过置信度过滤,多个返回结果可以优先取最大的一个。
  • 建议统一技术栈会比较好,减低团队整体的学习成本。

    1. 沿用 airtest 图像识别那一套,功能不支持的自己用 opencv+pyautogui 之类的库去补充。
    2. 如果更喜欢 selenium、appium 之类的 driver 形式,可以看看这个https://github.com/microsoft/WinAppDriver
  • 这个好像比较难,ui 控件属于表现层,可以明确抓到 xml 结构,但是接口通常都是由各种事件触发,纯探索测试的话很难模拟业务逻辑的交互,导致抓不到接口。

    另外接口的请求数据构造也是一件挺麻烦的事,光请求接口不起作用呀,要数据符合业务逻辑才行。

    目前见过的压测大致分单接口压测和全链路压测,一般单接口都比较费时间了,全链路更是要耗费很多时间和资源,可见压测并不简单,先专注做一件事情可能会好一些。

  • 大概写几点吧

    一、 新人文档:
    项目持续 5,6 年,积累了大量的文档但是发挥不了作用,问题点很明显:①文档太多,缺乏重点;②历史冗余、废弃文档,已经没什么用处;③业务知识太偏门,新人理解不了......

    关键点就在于需要一份精简的文档,让刚来的新人能看懂,能理解,能消化,抓最重要的点之后,答案就很明显了,文档一定是要和业务主流程或者行业基础知识或者职业基础知识相关的。每个公司的业务和流程都不相同,我就举一些简单的例子,比如:创建客户,补充资料,调整额度;下订单、完单;用例、缺陷管理;测试流程;工作关键规章制度等。

    新人入职前几天基本都是看文档,看完这些文档并有一定的尝试之后,后面做事虽然磕磕绊绊,但不至于完全卡住,就和写代码差不多,先搭个 demo 跑通,再来优化细节和微调架构。

    二、新人导师:
    对于新人,初入职场,可能什么业务、制度都不懂或者了解不够细致,稍不注意就会触碰红线。职级最低,没话语权,老油条一句不客气或者敷衍的话就能让他心里记挂几天。这种情况下如果没有专职导师,大多数新人遇到问题不敢多问,害怕暴露自己的缺点或者得罪人,所以就自己瞎琢磨,浪费时间,一些简单的任务到了 deadline 却发现主流程都还卡着。

    所以有必要安排一个或者多个态度积极,有责任心的老员工带新人,快速解答新人的问题,同时让新人没有心里负担地去探索问题、提出问题、解决问题。还可以安排新老员工一起完成任务,新人能完成多少任务都是其次,最重要的是要让新人在和老员工一起合作的过程中学习老员工的经验、技能。老员工在完成日常工作的同时还要带新人,一定要有补偿,可以在评绩效或者职级晋升的时候加分。

    三、培训制度:
    有些企业不提倡开会,不提倡搞培训,觉得员工就应该像齿轮一样连轴转,这非常不利于员工的成长和职业发展。长期下来,员工疲于工作,有空闲时间就开始报复性娱乐,没心思去成长,逐步拉低自己的上限;长期从事重复性的工作,缺乏足够的自信和能力承接高难度挑战,不敢创新,不敢突破。先是员工的落后,再是团队的落后,一直到公司、集团,最后是一个行业的没落,我也待过传统企业,深有感触。

    不管再怎么忙,一定要花时间搞培训,做分享,可以是业务知识、测试方法、工作流程等方方面面的分享,让员工有时间能停下脚步来拓展自己的眼界和思维,磨刀不误砍柴工,养兵千日,用兵一时。这一点很感谢我的第一任主管,他在我们业务不忙的时候让每个人都去讲自己分工部分的内容,包括基础概念,业务,测试方法等,我听得最认真,我作为一个刚入行的新人,大半年时间就在行业知识的广度和深度上超过了部门大部分人。他给我留下最深刻印象的一句话是 “不要让自己有不懂的”,当然人做不到全知全能,但是可以做到解决每一个遇到的问题,理解原理,搞清楚每一个遇到的知识点,我虽然履历并不出彩,但是面试从来都是裸面,熟悉的问题我能讲清楚细节和原理,不熟悉的问题也能答个大概。

    解决方案太多啦,想一百步不如踏出一步,可以慢慢尝试去着手做这件事,方法总比困难多,人多力量大,多听听新人和老员工的反馈,多思考,多践行,体系慢慢就建立起来啦。

  • UI 自动化中的分层设计 at 2021年11月08日

    飞哥之前的 UI 自动化文章都仔细品读过,理论 + 实践完美结合,读完后理清了许多概念,了解了许多能解决痛点的解决方案。这次的文章也是一样,分层清晰,组件层有创新且能解决痛点,最难得的是能去改善传统的架构,总结经验并分享。这些优质文章,对于我这样写了几年代码,各方面都有涉猎但水平局限于熟练/精通的人来说,作用非常大,些微思想上的启迪和论证比写几个月的项目还有用。

    最近自己也在写一个接口自动化 + 数据工厂结合的项目,分层也几乎差不多,传输层做各个系统的传参、鉴权等,对应组件层,接口层对应 PO,服务层差不多一样,之上还有测试用例层和数据脚本层。

  • UI 自动化中的分层设计 at 2021年11月08日

    组件层的主要功能已经讲解得比较清楚了,定位以及常用功能。是利用组件的特征以及传入关键参数去定位,常用方法的话就是避免在 PO 里面写太多控件操作的方法,就直接把操作方法写到组件层,比如一个 Form 的填充、提交,Table 的数据获取,排序,切换分页,筛选等等,把 PO 里面有关组件的属性获取和操作方法等下沉到组件层。

  • 仔细读一下 error msg,报错是讲 desiredCapabilities 这个配置不对,里面的 noReset 字段只能为 boolean 类型。

  • 全是我会的内容,你发帖问还不如直接问我 n_n,可以加个我微信:18084059840

  • 每天的工作就是培训、学习、写用例、评审、测试、站会
    投了多家简历后,最终去了一家外包公司做 SDET(测开)

    10 多年前,能有这么完善的流程,又能找到测开岗,感觉前两份工作还不错。。至少让自己有机会见识更多的东西。

  • 各个阶段的公司都待过,情况还是比较符合。

  • postman 可以写 js 代码呀,在 Pre-request Script 里面先清除 cookie 就行

  • 没事,我也是早上没事写点片段热热身,能解决您的问题真是极好的。

  • 格式怪怪的,如果没有特殊需求的话,建议直接用 json 库转,格式有略微不同的话,稍微调整下格式再转。
    自己随便写了点转换的代码,不敢写下去了,再写就要造一个 json 库轮子了。。

    import json
    
    text = "Lindar = \"text=light, position = True\", num=10, check_condition=False, Timeout=1000"
    
    
    def split_text(text: str) -> list[str]:
        start = 0
        pieces = []
        is_in_string = False
    
        for index, char in enumerate(text):
            if char == "\"":
                is_in_string = not is_in_string
            if char == "," and not is_in_string:
                pieces.append(text[start:index])
                start = index + 1
        # 添加最后一段
        pieces.append(text[start:])
        return pieces
    
    
    def combine_key_value(pieces: list[str]) -> dict:
        pairs = {}
        for piece in pieces:
            for index, char in enumerate(piece):
                if char == "=":
                    key = piece[:index]
                    value = piece[index + 1:]
                    key = key.strip()
                    value = value.strip()
                    pairs[key] = value
                    break
        return pairs
    
    
    def type_convert(text):
        pass
    
    
    pieces = split_text(text)
    pairs = combine_key_value(pieces)
    print(json.dumps(pairs))