专注测试技术的课程订阅站点

测试人员必会的docker秘籍

Docker现在是许多 QA 工程师的常用工具。它用于生产环境和测试环境或两者兼而有之。docker 的文档制作精良,相对容易理解,但有时我们需要一些常用命令来解决问题。

在这里我列举一下对于测试同学来说比较值得去弄明白的 docker 秘籍。

**如何使用不同的参数运行已经启动的 docker 容器?**

可以用这个工具:https://github.com/lavie/runlike/

使用方法: runlike -p <container_name> ,这样就可以拿到该容器第一次启动时候用的具体命令了。

runlike -p testservice
docker run \
--name=testservice \
--user=test \
--env=KAFKA_HOST=172.17.0.1:9092 \
--env=PATH=/opt/java/openjdk/bin:/usr/local/sbin:/usr/local/bin \
--env=LANG=en_US.UTF-8 \
--workdir=/home/testapp \
-p 8015:8080 \
--restart=always \
--log-driver=journald \
--runtime=runc \
--detach=true \
myrepo/testservice:master-1374

这时候我们就可以修改一些参数,比如端口号信息,生活变得容易了一些。

**如何在 docker 容器中运行本地 bash 脚本?**

cat local_script.sh | docker exec <container_name> /bin/bash

如何重启或移除所有的容器?

这个技巧非常管用,推荐牢记。

docker stop $(docker ps -a -q)
docker restart $(docker ps -a -q)

**如何清理旧的 docker 镜像、容器和卷?**

docker system prune -a

**如何过滤 docker ps 命令,以仅获取所需的信息,例如容器名称、状态和镜像?**

docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}"

**如何保存和恢复 docker 容器?**

docker commit -p <CONTAINER_ID> <YOUR_BACKUP_NAME>
docker save -o <CONTAINER_FILE>.tar <YOUR_BACKUP_NAME>
docker load -i <CONTAINER_FILE>.tar

如何将常用的 docker 命令简化成别名?

如果你是 docker 的重度用户的话,这个能力非常实用。

leetcode最有名的简单算法题:two sum

应该还是有很多同学在刷题找工作吧,如果大家刷 leetcode 的话,推荐的做法是从简单到难,这样一来 two sum 是大家绕不过去的最著名的简单算法题了,废话不多说,先看题目的描述。

给定一个整数数组  nums  和一个整数目标值  target,请你在该数组中找出  和为目标值 target  的那  两个  整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

提示:

  • 2 <= nums.length <= 104
  • 109 <= nums[i] <= 109
  • 109 <= target <= 109
  • 只会存在一个有效答案

**进阶:**你可以想出一个时间复杂度小于  O(n2)  的算法吗?

在2023使用playwright进行自动化测试

playwright 一直是我最看好的新一代自动化测试框架,2022 年底 playwright 在 npm 上的下载量超过了 100 万,尽管不如 selenium 和 cypress,不过势头还是相当强劲的。最近正好发现一篇文章简单的介绍了使用 typescript,pageobject 和 fixture 配合 playwright 进行用例编写的文章,这里把里面的精华拿出来分享一下。

老生常谈,playwright 的优势

  • 有个好爹,微软出品,看好长期更新维护和迭代,但也可能突然被砍掉,毕竟大公司都在裁员
  • 运行速度快
  • 自动等待元素出现
  • 报告的呈现很多元化,可以设置重试机制,捕获执行日志,截屏录屏等
  • 支持多个浏览器并行执行
  • 提供自动生成代码能力以及 Inspector GUI
  • 一套代码,跨浏览器执行的能力

目录结构

框架整体的目录结构如下。

.
├── config
   ├── global-setup.ts
   └── playwright.config.ts
├── package-lock.json
├── package.json
└── src
    ├── data
       └── data.json
    ├── fixtures
       ├── AxeFixture.ts
       └── TodoFixture.ts
    ├── pages
       └── TodoPage.ts
    └── tests
        ├── a11y.spec.ts
        └── demo-pom-todo-app.spec.ts

config 目录

  • playwright.config.ts playwright 的配置文件
  • **global-setup.ts** 在所有用例执行前运行一次,主要的目的是登录一次被测系统并保存浏览器的全局状态到 storageState.json 文件中。这样就不需要每个用例都去单独登录一次了。更多信息可以参考文档。https://playwright.dev/docs/test-advanced#global-setup-and-teardown

Page Object

po 基本上是自建框架的必选项了。具体的实现如下

强行为新项目写接口测试是一种什么样的体验

继续上次的话题,为新项目写 ui 自动化测试是一件非常有挑战的事情,写接口测试会不会容易一点呢?这次我就尝试了一下。

现阶段我们的管理端接口其实不多,就 8 个左右,所以从工作量上评估其实还可以。

测试策略

讲策略之前我们先看一下项目的简单业务属性。该项目的管理后台其实就是稍微复杂一点的增删改查。增加一条记录,编辑记录,各种组合条件查询记录,删除功能暂时没有,后面可能会跟进。

我的测试策略也很简单,首先搭建 1 个单独的测试环境,防止跟其他测试形式冲突,然后把最大的精力放在数据的准备和清理上。

  • 准备数据:为了测试查询,比较好的方式是每次都先清空数据库,然后动态创建一些固定的数据,我指的固定是比如固定 100 条,每条的排序规则,各个字段都是确定的。
  • 数据清理:完成测试后清空数据库,比如搜索测试完成之后清空数据库去测试创建和编辑,这样创建的时候就没有存量数据造成的断言干扰,就可以实现每次创建之前数据都是 0 条,创建成功之后变成 1,这样断言就相对容易,而且用例能够以随机的顺序运行,减少了依赖;

代码实现

这里最有挑战的是数据准备和清理的代码实现,我需要准备下面的数据

  • 通过接口创建一些数据到 db,之所以用接口创建是因为一些联动的操作直接写 db 的话无法触发。我们的接口是 http 的,用 python+requests 就可以了,稍微麻烦的点是构造的数据需要通过接口的有效性校验,比如时间区间之类的,因为我们的服务会跑在多个不同的国家和地区,timezone 也是需要关注的
  • 把创建成功的数据 id 以及一些需要用到的字段存到 redis 里。我习惯于用 redis 的 set,因为天生去重,并且可以使用srandmember来随机返回几条数据,在做查询校验的时候非常方便
  • 查询一些关联表的信息或者需要用到的数据,保存在 redis 里。这里我用的是 redis 的 string 类型,需要保存的信息直接序列化成 json 字符串,非常的方便;
  • 直接写 sql 做数据库的清理,直接调用 es 的 resetful api 做索引的清理。我们的查询使用的是 es,所以清理数据的时候除了用 sql 去 delete 之外,es 的 index 也是要清理的
  • 使用配置文件来进行多环境多地区的配置,比如 mysql 每个地区的配置都不一样,同样的地区不同的业务数据也在不同的库里,把配置弄起来还是很有必要的

最终实现的效果是运行TEST_ENV=test REGION=cn python data_builder.py这条命令之后就可以在对应的环境创建初始化数据了。代码比较啰嗦我就不粘贴了。

用例编写

用例编写无非是增删改查。

假如有一天我躺平了

最近躺平几乎成了内卷的反义词,我了解了一下躺平最初的由来,发现是源自百度贴吧,帖子的主人公过着消费极低而且自在悠闲的生活。没有钱了就去横店躺平扮演尸体,赚了一点点钱就不再工作,反正他的消费非常少,躺几天尸够他活个一阵子了。

这种生活方式突然受到了追捧,当然,很多人只是说说而已,真让他们去躺平难度还是非常大的,毕竟上有老下有小,现实总是不能允许你做任性的事情。这可能表现了喝彩者对内卷化下意识的反对和厌恶,也算是反内卷方程式的一个潜在的解法。

我不知道内卷是不是经济发展变缓这个特殊缓冲区的产物。因为之前经济高速增长,人们已经有了努力就能获得一切的惯性,当经济稍微停滞一些的时候,努力的惯性也许就成了内卷的核反应堆。等经济增速回归到一个较低水平的时候,也许各种花式躺平就成了主流,比如现在的台湾和日本,到时候可能一部分人卷上加卷,大部分人默默躺平。这也没什么不好,之前旅行的时候经过一些小城市,穿梭在人流中竟然感受到了许久不见烟火气,心里也曾经有过这样一个念头,假如有一天我躺平了,我会做些什么?

如何生存

做为一个测试人,我总是信奉一条原则,在项目越早期想的越多越清楚,后面的坑可能就会相对少一些。所以在躺平之前,我应该会重点思考一个问题,那就是生存还是毁灭的终极问题。

躺平了就意味着不能在职场打工干饭了,因为如果打工的话,你的身体告诉你要躺平,但你的老板却花式要求你要更加努力,修更多福报。

不能打工如何活下去呢?记得《穷爸爸富爸爸》里就有类似的讨论,不打工可能凭借自己的资产活下去,比如投资的公司的收入,隐形资产比如知识产权的收入。不过这些离我比较遥远,我觉得比较有可能的收入来源是

  • 稳健理财的收入。长期定期投入的话,可能会有年化 5%左右的收入吧;
  • 激进型理财的收入。比如股票和以股票为主的基金收入;我有个朋友曾经跟我分享过一个例子,他有个同事很多年前买了几百万的大头股,现在不仅股票涨了,而且每年都有十几万的分红,过的相当滋润,当然了,这是极其典型的幸存者偏差,大家听完一笑而过就好。另外我也见过有一对台湾夫妻分享他们财务独立的案例,他们定投了十几年的基金,后来财务独立环游世界,这个例子我找到了好几个佐证和类似的例子,所以应该有一定的可行性。好的年份激进型理财的年化收益可能会有 10%左右;
  • 零散收入。这部分收入可遇不可求,比如帮别人开发一些自动化工具或者自动化脚本之类的,随缘就好;
  • 潜在收入。学一门手艺,比如木工电工之类的,接一些零活;

总的来说收入主要靠投资和理财,其他的只能随缘,不能糊口。

下面来算支出。

  • 房租水电。在一线城市躺平成本比较高,而且周围的人在疯狂搞钱,受他们的影响容易焦虑,所以可以考虑搬去二线城市;一些优质教育资源集中的二线城市的头部学校可能比一线城市的普通学校综合实力更强,也能缓解一些鸡娃焦虑。那么二线城市的房租是多少呢?我随便搜了一下,好点的位置大概 5000 左右,如果稍微降低一下标准,算上水电等,一年 6 万应该是可以搞定的;
  • 吃饭。在深圳自己买菜的话,一天 100 块应该够 3 口之家吃的很好了;现在物价基本上大部分城市都差不多,所以在吃上面的支出一二线城市 100 块一天是差不多了,加上偶尔出去吃一顿,那么一年 4 万应该是可以搞定的;
  • 出行。去二线城市应该可以卖掉现在的车,因为一台车一年的成本差不多要 1 万块,然后换一个小电驴,周边的出行应该就没大问题了;出远门可以地铁或者打车,想开车自驾的话可以租车,租车很便宜,一天 100 多一点可以搞定。所以综合算来一年 1 万左右吧;
  • 穿衣。我自己的衣服很简单,随便找个店买点基本款就可以了,如果住在南方几件 t 恤就能过一年,所以一年穿衣千把块就可以了。算上全家的一年大概 1 万左右可以搞定;
  • 其他支出。比如换手机或者买点乱七八糟东西之类的,一年大概 2 万吧;

这样算下来,总支出应该是 6+4+1+1+2=14 万,再加上一些无法预计的支出,再放宽 2 万,一年的生活成本应该是 16 万。

如果有 400 万左右的存款,放 100 万去做高风险投资,年化 10%,300 万做稳健理财,大概 5%,这样一年的收入应该能有 25 万,粗茶淡饭一年生活下来,还有 9 万的盈余可以投入到下一年的激进理财里,因此除了跑不赢通胀的焦虑之外,拿 400 现金应该就可以躺平了吧。记得前不久有则新闻,一对北京土著夫妻卖了北京的房产,一家三口着 1000 万去昆明买房定居,其实算下投资收益,他们哪怕找不到工作一家三口平平淡淡过一生也应该足够了吧。

可以做些什么

不工作的话可以做的事情就很多了。

  • 旅行。可以自驾也可以公共交通,丰俭由人,预算够的话就稍微放飞一些,预算吃紧就经济一些,行万里路,给生活带来不同的节奏;
  • 读书。可以泡图书馆和书店,书非借不能读也;
  • 带娃。我前几天听说一个数据,中国家庭在义务制教育阶段对孩子的教育投资大概占家庭总收入的 12%左右,这还只是平均数,在一线鸡娃焦虑的城市这个数字只会更高,我估计可能有 20%以上,所以如果孩子不上各种补习班,自己带孩子出去旅行或者教点什么的话,每年节省的费用是可观的;不过自己教娃很难,我觉得这个比上班累;
  • 学一些实用的技能。比如木工电工自己理发之类的生存能力,做到万事不求人,这样长期看来也能节约不少支出;
  • 学一些不实用的技能。比如英语钢琴之类的,这样可以教娃,省培训班的钱;
  • 打游戏。我觉得我可以在家打一个月的足球经理或者三国志,足不出户;
  • 锻炼身体。这只是说说,哪怕有时间了坚持锻炼身体还是挺难的,不过好的身体可以省药钱,还是要锻炼的;

这样算下来,有 400 万存款就可以在一个二线城市非常平静的生活下去了,有这样经济能力的人不在少数,起码我身边就有不少。不过大家还是坚持在一线城市卷珠帘,低绮户,照无眠,我猜大概率还是有激情有理想,另外伴随着深深的通货膨胀以及教育焦虑吧。

做一个没有感情的用例生成机器

最近我们准备把一些服务从一个二次开发的 k8s 平台迁移到另一个二次开发的 k8s 平台去,这时候我们遇到了一个比较棘手的问题:因为迁移是有损的,也就是说迁移的过程中伴随着一定量的代码修改和容器编排方式的适配,那么我们如何保证服务迁移以后的功能是正常可用的呢?

想了一些方法,这些办法其实比较常见:

  • 增加监控指标和告警指标,迁移后如果服务有问题那么这些指标会让我们尽可能快的发现问题;
  • 增加自动化的测试用例,通过用例尽可能的对现有服务功能进行覆盖;

那么问题就来了,如何用最低的成本去实现这些自动化用例呢?

首先我们应该选择合适类型的自动化用例,目前我们可以实现的用例有

  • 单元测试用例,实现成本高,但是运行成本低,运行速度快,服务没有启动的情况下也能跑,如果有的选,那么单元测试用例是首选;
  • 接口级的测试用例,实现成本相对较低,运行成本高,需要服务跑起来才能进行测试,运行速度相对单元测试是慢一些的;
  • ui 自动化测试用例,实现成本高,运行成本也高,运行速度也慢;

最终我们选择增加一些核心的单元测试用例和 ui 自动化用例,然后尽可能多写接口测试用例。

我们的服务实现了一些微服务化,请求从最前面接入层进来之后会到 http 层,该层会调用更下层的 rpc 微服务层实现具体的业务逻辑,因此我们的接口就有两种,分别是

  • http 接口,客户端直接调用
  • rpc 接口,http 层以及微服务之间进行调用

因为业务逻辑往往需要多次的 rpc 调用才能实现,所以直接写 http 层的接口测试用例相对来说是一种比较经济的方式,因为一次的 http 接口调用会产生多次的 rpc 调用,从业务逻辑上和服务的触达性上来说都是令人满意的。

那么怎么去用尽可能最低的成本去实现这些 http 接口的用例呢?我之前有过使用 postman 把自己变成一个没有感情的用例生成机器的经历,在这里可以给大家分享一下。

举一个具体的例子,看这个接口。

POST /api/users

请求
{
  "user":{
    "username": "Jacob",
    "email": "jake@jake.jake",
    "password": "jakejake"
  }
}

响应
{
  "user": {
    "email": "jake@jake.jake",
    "token": "jwt.token.here",
    "username": "jake",
    "bio": "I work at statefarm",
    "image": null
  }
}

这是一个用户注册的 POST 接口,需要向后端传 json 类型的数据,我们可以看成是传递一个 user 对象,这个对象有 username, email 和 password 属性,用 json 字符串的形式表达出来而已。