• 首先,app 和电商是两个不同维度的东西吧,app 算技术领域,电商算业务,按这个来说年限有点怪怪的。

    其次,业务延续性确实不同人有不同的选择。我之前在互联网金融公司,里面的小伙伴出来有有继续在金融领域的,也有换到其他互联网领域的,甚至转行做产品或者其他岗位的。

    我自己每次跳,虽然一直都是测试领域,但从公司对应业务领域来说差别都挺大。个人感受上,其实不会有太大的 “业务没有延续性” 这方面的担忧,反而会觉得自己的业务观会越来越大,而这个业务观也会帮助自己每次熟悉新领域的时候,更快速能上手和找到关键点。

    补充一个点,对某个领域是否深入,呆多长时间只是一个参考值,关键还是是否有持续提升和扩大自己的视野。我在上家公司 3 年多时间,从小组长做到质量团队负责人,视野从只是管小组,变成了整个公司业务的质量,个人感觉对业务的深入度,尤其是对抓关键点的能力(比如那些功能对业务而言更重要),其实会越来越强。而这个能力后面换了公司,还是可以延续的。

  • 大厂面试总结 at 2021年07月05日

    包含但不限于这个,DevOps 关注的还是研发流程内部。这些年的实践,我对于提效的理解也有些转变,有点朝着精益的方向。

    除了重复事情自动化外,会更多考虑怎么 “简单做”(简单到别人也能做,就可以顺势赋能),甚至 “不用做”(比如基于投入产出比评估来砍/拆需求、降低技术方案复杂度,这样质量风险减少,测试工作量也能对应减少)。

  • 大厂面试总结 at 2021年07月05日

    以我这几年做测开的经验, 很多有价值的事情都是需要一定的研发和运维能力才能去做。

    这句想特别点个赞!很多时候想要做更有价值的事情,得先自己突破测试的界限。

  • 我们以前遇到过类似场景,自己封装了一个断言函数,大概逻辑是:
    1、入参是超时时间、查询 sql 、预期值
    2、在超时时间内,每隔 1s 去查数据库的值,并确认值是否符合。不符合就继续查询,符合就 return
    3、达到超时时间后的下一次查询,还是不符合,就抛出断言异常

    借鉴的是 selenium 的 WebDriverWait 的思路。如果想通用性更强(比如不只是查库,还可以是其它断言),推荐直接引入 awaitility ,这个库就是专门干这个活的。

  • 这个实践挺不错的,把重复繁琐的工作用自动化来提效。

    手动筛选映射关系模板,人工核对代码的输出结果跟筛选的结果是否一致

    这个部分能否详细说下在这个场景下,人工是怎么测试的验证的?

    感觉这个 “筛选”,就是从庞大的映射集关系里,找到 key 和sku_key_list一样的规则,并把sku_list里对应的值,和规则里对应的值做一次匹配,得出对应的人可以理解的含义?筛选出这些规则后,后续是怎么测试?

  • 我 2 年多出来的时候,也是差不多这个水平。这个薪酬和年限要求,薪酬不算低了吧。

    话说,你那个朋友 9 年经验,这个 1-3 年的岗位也不一定匹配他把,忽略就好。真正要过百万那种高薪,基本都在大厂核心部门 + 高级别岗位。

  • 客气啦,这些我也是和其他人交流 + 自己实践学到的,后面可以来社区多分享交流。

  • 客气了,后面可以在社区持续交流。不仅是技术,也可以是一些提效的小技巧。

    很多时候,通用性强(外面各种分享常见的基本都是这类)的工具,成本也不低。针对特定业务特定场景做的一些小工具,小改进,反而效果会更明显。

    个人觉得,对于能效领域,工具平台都是辅助工具,关键点还是思想思维。比通过工具来提高测试效率来得更有效的,是通过需求分析、技术设计直接降低工程的复杂度,进而减少测试、开发工作量。

  • 补充一个在实际中用处特别大的平台:环境部署管理平台。
    主要提供能力:
    1、快速部署单个应用(在部署里面融合流水线做各种卡点)
    2、根据当前客户端绑定的环境,自行切换路由能力(比如完整系统是 a+b+c,本次需求只改了 b,那这个需求专属环境只需要部署 b,a 和 c 自动用公共环境里已有的)

    这个平台在团队并行需求多的情况下,能非常有效提升效率,彻底解决测试环境少了不够用、多了部署和维护成本高的问题。

  • 都是挺好想法。前 4 个点也很认同。现在为了 kpi 而做的平台已经越来越少了,也是一个好事。

    个人觉得除了看到有这些业内有成功经验的能效平台外,还需要关注是否适合公司当前的业务,落地的 ROI(投入产出比)是否合适。上面提到的平台很多都是外部分享的常客,但有不少目前没有对应的可开箱即用的开源实现,所以对于不少公司来说,需要自行开发的部分不少,成本还是不低的。

    比如流量回放平台
    主要成本:找到运维 + 开发进行对应系统调整及接入,以及结果比对里如何有效降噪。由于业内目前没有什么开箱即用级别的开源工具,需要自行整合和适配自己公司的基础框架,开发成本会比较高
    主要收益:对于已有流量的接口,可以快速构造对应的真实测试用例
    ROI 最高的场景:接口不变情况下的系统重构/代码持续优化
    总结:如果接下来有大型的系统重构,或代码持续优化是开发团队比较关注的点,且一直会占据一定的比例,带来一定测试工作量,适合使用。如果没这类场景,或者这类场景比重较低(基本都在做新功能,不怎么还技术债),投入产出比不高。

    比如代码覆盖率平台
    主要成本:测试环境的覆盖率数据接入、覆盖率统计平台开发、个别个性化能力的开发(比如增量覆盖率、多版本覆盖率合并等)、测试人员额外投入时间分析未覆盖点风险。
    主要收益:覆盖率可以作为测试范围是否充分的参考指标;未覆盖行的分析可以辅助发现漏测点。
    ROI 最高的场景:测试全面度的指标统计、测试末期的未测试点补充
    总结:团队对质量要求较高,不仅要求需求点都有测试,且要求开发的一些功能点(比如异常处理流程)也需要覆盖时,收效会比较明显。如果还没到达这个阶段,或者相比精细化测试更关注快速交付,不一定适合。

  • 有两种方式。

    一种是你说的,去查询数据库。
    另一种也是你目前在做的,使用查询接口。

    两种都可以,相对来说查询数据库会更自由(因为系统可能没提供查询接口,或者部分内部字段客户端/前端用不到,查询接口不会暴露出来)。不过如果查询接口可以满足,用查询接口是最好的,后续有线上接口拨测需要的时候,可以直接复用(运维是不大可能为了这个,给你开线上数据库的权限的)

    至于分页这个,抽验几个点就可以了:
    1、总页数对不对
    2、改变当前页数,数据是否有对应变化

    实际开发写代码实现分页逻辑,这方面都有对应的库(可以百度下 mysql 分页查询),库的写法对了就很稳了,配置不对上面两个数据其中一个基本都会出问题,也很容易发现。

  • 某篇匿名是我写的 at 2021年06月29日

    其实你说的东西是有一定道理的,比如从看技术书开始,学习要不断否定自己,这些我都认同。确实相比算法这类技术要求很高的岗位,自己还很菜。

    只是这个表述方式有点太居高临下的感觉了,甚至略带点嘲讽(特别是 哎,挺难的,我这要求确实太高了 这一句,可能希望起到激将法,但可能听起来更像是打上了你们就是做不到的标签)。这语气更多会引起反抗,而非反思。

  • 某篇匿名是我写的 at 2021年06月29日

    辩论不等于争论,更不等于要说服别人哈。个人理解,辩论的目标不是要辩赢谁,而是要看到事物的两面性,让大家对这个问题产生新的理解。

    主要是看到大家戾气有点强,求学态度慢慢变弱了,所以才想搞下这样的活动。我比较喜欢 “三人行必由我师” 这种态度。

    至于鄙视这个点,其他人怎么鄙视测试那是其他人的观点,也确实会天外有天人外有人。单纯语言上的反驳收效是很微小的,自己持续想办法变强,证明自己就好了。

  • 看来涉及到了不少 pytest 特性,这块不大熟悉,先退下了。

    看看其他同学有没有什么建议把。

  • 额,从你的截图看,你这个代码里,self.staue 没可能是 False 。你这里逻辑是不是没贴全?

    1、初始值是 True
    2、后续改变值的语句 self.statue = outcome.success ,是放在了 if outcome.success 里面的,意味着这里面的 outcome.success 值如果是布尔值,那也必定是 True

    所以,你在后面继承的 B 类里拿到 self.statue 是 True ,说明不了这个 True 就是第二个位置给的。

    然后,也没太看懂你这里加个 self.statue 且想要被子类拿到的目的是什么,想实现什么功能?这块可以说下不?

    PS:statue 是不是拼错了?状态的英文应该是 status

  • 这个要看公司。没法一概而论。也有的直接按各个团队人员比例来裁。

  • 你这个方法,fun4() 能拿到的只有 init 里配置的初始值,拿不到 fun2() 里面改变后新的属性值吧。fun2() 改变的是 class A 对象里面的内部属性,而 class B 继承的是类,而非对象。

    而且即使能实现,你这块设计也会导致使用时比较混乱。因为你这个属性值有可能在 class A 的任何方法里被改动,而且这个 class A 极有可能是在其它文件里的,对于 class B 的使用人员来说,这个属性值是一个难以预测的值,要合理使用还得看完 class A

    建议可以考虑下,修改 class A ,提供这个属性值的 get 方法。然后 class B 通过获取 class A 实例化后的对象,来获取对象里面的属性(可以通过单例模式来保障 class A 只会有一个对象,避免存在多个对象拿错了)。而不是通过继承去获取。继承只能继承类,复用里面类相关的资料(如方法、类变量这类代码里本身就有写好的内容),但没法继承一个对象的(也就是用不了任何运行时才创建或改变的内容)。

    或者可以去掉这些 class A 之类的脱敏,具体说下你这个场景是什么,这样才能更好地给合适的建议。

  • 1、这种算是业务逻辑。因为不能保障和看到的接口文档强一致,不过这类逻辑如果是非常简单的,也建议直接 review 代码。个人觉得,不是很值得为了一个超简单且不怎么改的业务逻辑,去维护一个用例。

    2、这种我理解就是多接口调用测试用例了,也就是上面很多同学说的接口集成测试,或者叫流程型用例(对比单接口用例)。一般做法是在 setup 里面调了 A 接口后,从 A 接口的返回值提取需要的参数。然后在 test 方法里把参数带上去请求 B 接口。

    PS:你这种不是 callback 吧?个人理解的 callback 是被调用方在完成自己需要做的东西后,主动发起某个调用方明确要求的操作,主要用于异步非阻塞型任务。你这个 B 接口要 A 接口返回值,前提是 A 接口已经有返回,所以是同步阻塞型的

    比如金融业务里常见的付款,A 系统调用 B 系统提供的付款接口,并要求在 B 系统处理完毕后给某个指定的 url 发一个请求,表示处理完毕。B 系统会在收到后立即放到一个类似队列的位置缓存,然后返回已收到(此时还没开始处理),此时这个 api 调用已经结束了。接下来 B 会做一系列操作(比如请求第三方等),全部操作完毕后,再调用最前面 A 系统给的一个指定 url,通知自己已经处理完毕。这个在最后主动调用 A 系统给的 url,我理解才是 api callback 。

    同步异步、阻塞非阻塞的解释,建议看这篇文章,讲得非常清晰:https://mp.weixin.qq.com/s/CvImJ5Ab1J7KiKAhV0BuAQ

  • 发帖提示什么?

  • 以下均为个人观点哈

    • 第一个问题,要看你的被测系统是怎么实现这类错误的。

    比如 url 错误返回 404,大部分情况是 web 框架(如 springmvc)就自动返回的,开发啥都不用写,或者写一次就可以永久使用。这种个人觉得不需要测试

    但也有的 url 是直接通配符匹配后,在具体开发写的业务逻辑里面进一步处理的(比如 path 参数,/user/{userId}/info这类,实际 url 会是 /user/1/info,也会是 /user/2/info),那就要测试 url 里面 userId 存在、不存在 2 种场景。

    一般异常场景,会区分为业务逻辑异常/非业务逻辑异常。非业务逻辑异常主要是由框架直接校验的(如某某参数不能为空,swagger 或者 jsr303 注解,controller 定义接口的时候就可以一并完成校验了),这类抽查或者通过 review 代码确认有没有问题,更高效;业务逻辑异常则是具体的内部逻辑,这部分一般会是复杂度比较高的,做接口测试会更高效。

    • 第二种问题,要看实际业务系统里面,要调用这个接口的服务/客户端,是怎么拿到这个 api 不会给出的值的?

    如果拿的方式是找后端拿(比如不走 api 取,但是走 mq 取,甚至直接查库),那你就找后端拿

    如果拿的方式是本身自己内置(比如本身有字典表或者对应常量),那就直接写死在你代码里

    至于你说的 “自己写一个 mock 的接口” ,不是很明白。你还是把你的完整场景说清楚吧,现在说一半不说一半,看不大懂。

  • 我们这边实际用远程启动好 wda 服务时的日志:

    2021-06-18 18:55:34:442 [Appium] Welcome to Appium v1.21.0
    2021-06-18 18:55:34:443 [Appium] Non-default server args:
    2021-06-18 18:55:34:444 [Appium]   port: 20000
    2021-06-18 18:55:34:444 [Appium]   sessionOverride: true
    2021-06-18 18:55:34:444 [Appium]   logFile: public/serverLog/appium_20000_2021-06-18_18-55-33.log
    2021-06-18 18:55:34:444 [Appium]   localTimezone: true
    2021-06-18 18:55:34:461 [Appium] Appium REST http interface listener started on 0.0.0.0:20000
    2021-06-18 18:55:34:634 [HTTP] --> GET /wd/hub/status
    2021-06-18 18:55:34:634 [HTTP] {}
    2021-06-18 18:55:34:635 [GENERIC] Calling AppiumDriver.getStatus() with args: []
    2021-06-18 18:55:34:636 [GENERIC] Responding to client with driver.getStatus() result: {"build":{"version":"1.21.0"}}
    2021-06-18 18:55:34:639 [HTTP] <-- GET /wd/hub/status 200 5 ms - 68
    2021-06-18 18:55:34:640 [HTTP] 
    2021-06-18 18:55:35:066 [HTTP] --> POST /wd/hub/session
    2021-06-18 18:55:35:066 [HTTP] {"desiredCapabilities":{"noReset":true,"xcodeOrgId":"F7YWW93M6T","bundleId":"xxx","skipLogCapture":true,"deviceName":"iPhone6s","wdaLocalPort":25000,"webDriverAgentUrl":"http://192.168.25.12:20474","waitForQuiescence":false,"newCommandTimeout":43200,"platformVersion":"14.5.1","automationName":"XCuiTest","useNewWDA":false,"wdaStartupRetries":0,"platformName":"iOS","udid":"xxx","wdaConnectionTimeout":1800000,"autoAcceptAlerts":true},"capabilities":{"firstMatch":[{"appium:autoAcceptAlerts":true,"appium:automationName":"XCuiTest","appium:bundleId":"xxx","appium:deviceName":"iPhone6s","appium:newCommandTimeout":43200,"appium:noReset":true,"platformName":"ios","appium:platformVersion":"14.5.1","skipLogCapture":true,"appium:udid":"xxx","appium:useNewWDA":false,"waitForQuiescence":false,"appium:wdaConnectionTimeout":1800000,"appium:wdaLocalPort":25000,"appium:wdaStartupRetries":0,"appium:webDriverAgentUrl":"http:/...
    2021-06-18 18:55:35:066 [W3C] Calling AppiumDriver.createSession() with args: [{"noReset":true,"xcodeOrgId":"F7YWW93M6T","bundleId":"xxx","skipLogCapture":true,"deviceName":"iPhone6s","wdaLocalPort":25000,"webDriverAgentUrl":"http://192.168.25.12:20474","waitForQuiescence":false,"newCommandTimeout":43200,"platformVersion":"14.5.1","automationName":"XCuiTest","useNewWDA":false,"wdaStartupRetries":0,"platformName":"iOS","udid":"xxx","wdaConnectionTimeout":1800000,"autoAcceptAlerts":true},null,{"firstMatch":[{"appium:autoAcceptAlerts":true,"appium:automationName":"XCuiTest","appium:bundleId":"xxx","appium:deviceName":"iPhone6s","appium:newCommandTimeout":43200,"appium:noReset":true,"platformName":"ios","appium:platformVersion":"14.5.1","skipLogCapture":true,"appium:udid":"xxx","appium:useNewWDA":false,"waitForQuiescence":false,"appium:wdaConnectionTimeout":1800000,"appium:wdaLocalPort":25000,"appium:wdaStartupRetries":0,"appium:webDriverAgentUrl":"http://192.168.25.12:20474","appium:xc...
    2021-06-18 18:55:35:067 [BaseDriver] Event 'newSessionRequested' logged at 1624013735067 (18:55:35 GMT+0800 (China Standard Time))
    2021-06-18 18:55:35:069 [BaseDriver] The following capabilities are not standard capabilities and should have an extension prefix:
    2021-06-18 18:55:35:070 [BaseDriver]   skipLogCapture
    2021-06-18 18:55:35:070 [BaseDriver]   waitForQuiescence
    2021-06-18 18:55:35:729 [Appium] Appium v1.21.0 creating new XCUITestDriver (v3.43.0) session
    2021-06-18 18:55:35:729 [Appium] There are no active sessions for cleanup
    2021-06-18 18:55:35:731 [BaseDriver] W3C capabilities and MJSONWP desired capabilities were provided
    2021-06-18 18:55:35:731 [BaseDriver] Creating session with W3C capabilities: {
    2021-06-18 18:55:35:731 [BaseDriver]   "alwaysMatch": {
    2021-06-18 18:55:35:731 [BaseDriver]     "platformName": "ios",
    2021-06-18 18:55:35:732 [BaseDriver]     "appium:skipLogCapture": true,
    2021-06-18 18:55:35:732 [BaseDriver]     "appium:waitForQuiescence": false,
    2021-06-18 18:55:35:732 [BaseDriver]     "appium:autoAcceptAlerts": true,
    2021-06-18 18:55:35:732 [BaseDriver]     "appium:automationName": "XCuiTest",
    2021-06-18 18:55:35:732 [BaseDriver]     "appium:bundleId": "xxx",
    2021-06-18 18:55:35:732 [BaseDriver]     "appium:deviceName": "iPhone6s",
    2021-06-18 18:55:35:732 [BaseDriver]     "appium:newCommandTimeout": 43200,
    2021-06-18 18:55:35:732 [BaseDriver]     "appium:noReset": true,
    2021-06-18 18:55:35:732 [BaseDriver]     "appium:platformVersion": "14.5.1",
    2021-06-18 18:55:35:732 [BaseDriver]     "appium:udid": "xxx",
    2021-06-18 18:55:35:732 [BaseDriver]     "appium:useNewWDA": false,
    2021-06-18 18:55:35:733 [BaseDriver]     "appium:wdaConnectionTimeout": 1800000,
    2021-06-18 18:55:35:733 [BaseDriver]     "appium:wdaLocalPort": 25000,
    2021-06-18 18:55:35:733 [BaseDriver]     "appium:wdaStartupRetries": 0,
    2021-06-18 18:55:35:733 [BaseDriver]     "appium:webDriverAgentUrl": "http://192.168.25.12:20474",
    2021-06-18 18:55:35:733 [BaseDriver]     "appium:xcodeOrgId": "F7YWW93M6T"
    2021-06-18 18:55:35:733 [BaseDriver]   },
    2021-06-18 18:55:35:733 [BaseDriver]   "firstMatch": [
    2021-06-18 18:55:35:733 [BaseDriver]     {}
    2021-06-18 18:55:35:733 [BaseDriver]   ]
    2021-06-18 18:55:35:733 [BaseDriver] }
    2021-06-18 18:55:35:742 [BaseDriver] Session created with session id: 1f9c9767-7842-4635-8610-7adc9eee6738
    2021-06-18 18:55:35:761 [XCUITest] Current user: 'lizhi'
    2021-06-18 18:55:35:774 [XCUITest] Available devices: xxx
    2021-06-18 18:55:35:775 [XCUITest] Creating iDevice object with udid 'xxx'
    2021-06-18 18:55:35:775 [XCUITest] Determining device to run tests on: udid: 'xxx', real device: true
    2021-06-18 18:55:35:775 [XCUITest] Normalized platformVersion capability value '14.5.1' to '14.5'
    2021-06-18 18:55:35:776 [BaseDriver] Event 'xcodeDetailsRetrieved' logged at 1624013735775 (18:55:35 GMT+0800 (China Standard Time))
    2021-06-18 18:55:35:776 [BaseDriver] Event 'appConfigured' logged at 1624013735776 (18:55:35 GMT+0800 (China Standard Time))
    2021-06-18 18:55:35:776 [BaseDriver] Event 'resetStarted' logged at 1624013735776 (18:55:35 GMT+0800 (China Standard Time))
    2021-06-18 18:55:35:777 [XCUITest] Reset: fullReset not set. Leaving as is
    2021-06-18 18:55:35:777 [BaseDriver] Event 'resetComplete' logged at 1624013735777 (18:55:35 GMT+0800 (China Standard Time))
    2021-06-18 18:55:35:777 [WebDriverAgent] Using WDA path: '/Users/lizhi/.nvm/versions/node/v12.22.1/lib/node_modules/appium/node_modules/appium-webdriveragent'
    2021-06-18 18:55:35:777 [WebDriverAgent] Using WDA agent: '/Users/lizhi/.nvm/versions/node/v12.22.1/lib/node_modules/appium/node_modules/appium-webdriveragent/WebDriverAgent.xcodeproj'
    2021-06-18 18:55:35:782 [XCUITest] 'skipLogCapture' is set. Skipping starting logs such as crash, system, safari console and safari network.
    2021-06-18 18:55:35:782 [XCUITest] Setting up real device
    2021-06-18 18:55:36:266 [DevCon Factory] Requesting connection for device xxx on local port 20474
    2021-06-18 18:55:36:266 [DevCon Factory] Cached connections count: 0
    2021-06-18 18:55:36:266 [DevCon Factory] Successfully requested the connection for xxx:20474
    2021-06-18 18:55:36:267 [XCUITest] Starting WebDriverAgent initialization with the synchronization key 'XCUITestDriver'
    2021-06-18 18:55:36:268 [XCUITest] Trying to start WebDriverAgent 1 times with 10000ms interval
    2021-06-18 18:55:36:268 [BaseDriver] Event 'wdaStartAttempted' logged at 1624013736268 (18:55:36 GMT+0800 (China Standard Time))
    2021-06-18 18:55:36:268 [WebDriverAgent] Using provided WebdriverAgent at 'http://192.168.25.12:20474'
    2021-06-18 18:55:36:270 [WD Proxy] Matched '/status' to command name 'getStatus'
    2021-06-18 18:55:36:270 [WD Proxy] Proxying [GET /status] to [GET http://192.168.25.12:20474/status] with no body
    2021-06-18 18:55:36:287 [WD Proxy] Got response with status 200: {"value":{"message":"WebDriverAgent is ready to accept commands","state":"success","os":{"testmanagerdVersion":28,"name":"iOS","sdkVersion":"14.5","version":"14.5.1"},"ios":{"ip":"192.168.17.178"},"ready":true,"build":{"time":"May 25 2021 19:44:41","productBundleIdentifier":"com.facebook.WebDriverAgentRunner"}},"sessionId":null}
    2021-06-18 18:55:36:288 [BaseDriver] Event 'wdaSessionAttempted' logged at 1624013736288 (18:55:36 GMT+0800 (China Standard Time))
    2021-06-18 18:55:36:288 [XCUITest] Sending createSession command to WDA
    2021-06-18 18:55:36:289 [WD Proxy] Matched '/session' to command name 'createSession'
    2021-06-18 18:55:36:289 [WD Proxy] Proxying [POST /session] to [POST http://192.168.25.12:20474/session] with body: {"capabilities":{"firstMatch":[{"bundleId":"xxx","arguments":[],"environment":{},"eventloopIdleDelaySec":0,"shouldWaitForQuiescence":false,"shouldUseTestManagerForVisibilityDetection":false,"maxTypingFrequency":60,"shouldUseSingletonTestManager":true,"shouldTerminateApp":true,"defaultAlertAction":"accept"}],"alwaysMatch":{}}}
    2021-06-18 18:55:37:911 [WebDriverAgent] Parsed BUILD_DIR configuration value: '/Users/lizhi/.nvm/versions/node/v12.22.1/lib/node_modules/appium/node_modules/appium-webdriveragent/DerivedData/WebDriverAgent/Build/Products'
    2021-06-18 18:55:37:911 [WebDriverAgent] Got derived data root: '/Users/lizhi/.nvm/versions/node/v12.22.1/lib/node_modules/appium/node_modules/appium-webdriveragent/DerivedData/WebDriverAgent'
    2021-06-18 18:55:38:785 [WD Proxy] Got response with status 200: {"value":{"sessionId":"BE97DA3B-EB3D-4D16-8231-A8D18AA33E64","capabilities":{"device":"iphone","browserName":" ","sdkVersion":"14.5.1","CFBundleIdentifier":"com.apple.springboard"}},"sessionId":"BE97DA3B-EB3D-4D16-8231-A8D18AA33E64"}
    2021-06-18 18:55:38:785 [WD Proxy] Determined the downstream protocol as 'W3C'
    2021-06-18 18:55:38:786 [BaseDriver] Event 'wdaSessionStarted' logged at 1624013738786 (18:55:38 GMT+0800 (China Standard Time))
    2021-06-18 18:55:38:786 [BaseDriver] Event 'wdaStarted' logged at 1624013738786 (18:55:38 GMT+0800 (China Standard Time))
    2021-06-18 18:55:38:786 [XCUITest] Skipping setting of the initial display orientation. Set the "orientation" capability to either "LANDSCAPE" or "PORTRAIT", if this is an undesired behavior.
    2021-06-18 18:55:38:786 [BaseDriver] Event 'orientationSet' logged at 1624013738786 (18:55:38 GMT+0800 (China Standard Time))
    2021-06-18 18:55:38:786 [BaseDriver] The value of 'elementResponseAttributes' setting did not change. Skipping the update for it
    2021-06-18 18:55:38:787 [BaseDriver] The value of 'shouldUseCompactResponses' setting did not change. Skipping the update for it
    2021-06-18 18:55:38:787 [Appium] New XCUITestDriver session created successfully, session 1f9c9767-7842-4635-8610-7adc9eee6738 added to master session list
    2021-06-18 18:55:38:788 [BaseDriver] Event 'newSessionStarted' logged at 1624013738787 (18:55:38 GMT+0800 (China Standard Time))
    2021-06-18 18:55:38:788 [W3C (1f9c9767)] Cached the protocol value 'W3C' for the new session 1f9c9767-7842-4635-8610-7adc9eee6738
    2021-06-18 18:55:38:788 [W3C (1f9c9767)] Responding to client with driver.createSession() result: {"capabilities":{"webStorageEnabled":false,"locationContextEnabled":false,"browserName":"","platform":"MAC","javascriptEnabled":true,"databaseEnabled":false,"takesScreenshot":true,"networkConnectionEnabled":false,"platformName":"ios","skipLogCapture":true,"waitForQuiescence":false,"autoAcceptAlerts":true,"automationName":"XCuiTest","bundleId":"xxx","deviceName":"iPhone6s","newCommandTimeout":43200,"noReset":true,"platformVersion":"14.5.1","udid":"xxx","useNewWDA":false,"wdaConnectionTimeout":1800000,"wdaLocalPort":25000,"wdaStartupRetries":0,"webDriverAgentUrl":"http://192.168.25.12:20474","xcodeOrgId":"F7YWW93M6T"}}
    2021-06-18 18:55:38:789 [HTTP] <-- POST /wd/hub/session 200 3723 ms - 736
    

    不过我们是在 mac 下运行的,不会出现你上面日志里的错误:[WebDriverAgent] Cannot retrieve WDA build settings. Original error: spawn xcodebuild ENOENT 。建议你根据日志看下源码,看是不是 appium 会不会不管是否要不要启动 wda ,都要检测 xcodebuild 是否存在?

    如果是,要不改源码,要不可以往 path 搞个假的 xcodebuild ,让输出符合 appium 检测结果就行。

  • 可能我表述也不够清晰。我不是说不涉及这些规范,而是这些规范并不像是导致 迁云时间跨度长 的根本原因。 “因为迁云时间跨度太长了,所以我们要落实代码规范/code review”,这个因果关系不够明确,也说服不了别人。

    对于项目跨度时间长,因素会很多。而且很多时候这些地方缺少的不是规范或流程,而是大家的意识。好的程序员,并不会因为规范的缺失而写出很烂的代码,因为规范已经在他意识里面了,没规范他自己也会去制定规范。而改变意识成本是很高的,周期也比较长,需要找好节奏,循序渐进,并且每一步都找到合适切入点。

  • 明天回公司看下 appium 版本,感觉像是 appium 版本不同导致的问题。

  • 听你描述,学 java 至少可以看开发代码,修下小 bug 之类的。学 go 好像能做的事情更少?

  • 感觉你的问题和原因有点对不上呀。迁云我理解更多是运维部署层面的,最多和发布管理有一些关系,前面的这些不规范和迁云有什么关系呢?比如代码命名不规范,主要影响其他人的阅读和理解,但不会直接造成缺陷的。建议要先复盘清楚,到底根本原因是在哪里,没有解决根因,其他改进都是吃力不讨好。

    另外,要特别留意,这些规范,限制的不仅自己下面的测试团队,更多是开发、运维这些兄弟团队。先和大家沟通好达成共识,这个是大前提。

    我按我经历过的大概写下吧:

    • 代码规范

    阿里、google 这些大公司都有各个语言的代码规范白皮书这类文档分享出来的,各个语言自己也有自己的一些推荐规范(比如 idea 自带的规则基本是基于这些规范),可以基于这些来调整。我们之前具体的调整是由一些资深开发 + 测试组成的技术委员会来操刀,保障调整的有效性。而且这些人参与了制定过程,后面落地认同感也更强,更容易推进落地。

    同时也可以搞一些 bad case 之类的文化活动,分享一下某些不规范的写法会导致什么样的 bug ;或者组织大家看看 clean code 这本书,对怎么写好代码有更好的认识。

    • 分支规范

    这个核心就是分支模型了。一般主流就几个,主干开发 + 主干发布,分支开发 + 主干发布,主干开发 + 分支发布。一般需求并行比较多的,采用 分支开发 + 主干发布 会比较多。然后配合 gitlab 提供的 protect 分支特性,可以把主干保护起来,只有 master 权限才能合并,普通开发改不了,只能在自己的分支上修改,然后提 merge request(后面缩写为 MR)来申请合并。

    master 一般由团队里对系统比较熟悉的人来做,这样在 MR 的时候做的 code review ,会更有效。同时 MR 也可以定制补充一些自动检查项(如前面大家提到的 sonar 、单测这类),检查结果直接呈现在 MR 中,作为 code review 的参考。

    • code review

    这里面东西比较多,比如 review 的时机(赶早不赶晚,谁都无法认真 review 改动行数过千的改动),review 的方式(评审人和写代码的人一起的方式,如 review 会议、结对 review;不在一起的方式,如前面提到的用 MR review ),review 的限制程度(阻塞式,必须 review 通过才能合并和提测,节奏相对慢的用得比较多;非阻塞式,可以提测后再 review,节奏快的更适用)。这块建议微信公众号搜索下吧,以前看到过挺多好文章的。

    我们实际落地更多是提测后测试去阅读代码(我们一般不叫 review ),有问题会和开发沟通确认。通过阅读代码可以了解实现逻辑,也能更直接的找到一些问题,比如空指针隐患、if else 的场景不够全、事务性操作没有包到一个事务里等。

    • 版本发布

    不知道你们那边运维发布相关的基础设施做到什么程度,是已经做到了一个带审批流程的平台上,还是只是一个 jenkins job ,还是更人工的。如果想 QA 在这个层面上做很大的限制(比如二次确认开发有没有在最后时刻自己加料啥的),那需要在发布的审核流程里加上 QA 这个环节,QA 审核通过才能到实际部署这个步骤。

    我们实际落地,审核流程会有 QA 环节,并且 QA 不能只是管审核,还需要在上线后和开发、产品一起通过线上测试/指标监控等手段,确认上线后没问题,为最终上线结果负责。

    • 最后

    代码规范、分支规范、code review 这些,本质上更多是在开发阶段做的事情,如果本身开发都没这方面经验甚至想法,需要长期灌输慢慢接受。

    要逐步落地,建议可以先从线上问题复盘改进开始,一方面都出事故了,大家容易配合推进;另一方面这些问题一般也是破坏性比较大,最需要解决的问题。

    个人经验,任何规范都是约束,都是会产生额外成本的,而且落地最终要靠的还是人。所有的要推行的规范,一定要能解决某些目前存在的实际问题,并和大家取得共识。切勿为了规范而规范,最后变成一张废纸。

    PS:正常一个稍微大一点的开发团队,分支规范应该是最基础的协作基础,不可能没有的。建议你先和开发团队沟通下,了解下对于一个新开发进入团队,他们会教些什么,这里面一般就会有代码规范、分支规范这些了。