见过许多转行做测试的,但是三年从转行测试小白做到公司内测试团队总监候选,我想应该没几个
友商一家公司有一个传奇人物,和大多数人转行测试开始工作一样,她也是从外包开始干起,在此不好指出这个人,毕竟做过外包在正统的计算机专业出生眼中,总容易被看低一头,并不想因为这篇文章,让他被戴上偏见的帽子,就称呼其为黄某
没技术、没经验、没高学历、没正经公司要,只有从外包开始干起,甚至连外包都没得选,只是单纯的被外包公司挑来挑去
黄某25岁销售专业转行,刚进外包之初,她给自己的人生规划凸显出了她比之毕业大学生更丰富的阅历
作为一个自学测试知识转行后的第一份工作,她并不表现得很兴奋,因为她很清楚这是一个外包公司,从她入职的第一天,就没有过“我需要为这个公司这个行业努力打拼奉献一生”的觉悟
她说那时的她很清楚,软件测试连互联网的门都没进,所在的外包公司更是只能远远地看着互联网测试的大门,若是说互联网行业是一个小院子,那么此时的她仅仅只是门外遥之千里的一个无名旅人,离互联网行业还差的远
黄某转行的理由很简单也很实在,那就是赚钱,在信息时代,她觉得互联网的红利自己是可以赶上的,有了明确的目的,她便开始学习和互联网行业有直接关系的东西——一门编程语言
她自学的方式比许多人明智,想到自己就是为了工作,那么就为了岗位而学习
她去各种网站、各种平台、各种app上找测试需求的技能,看见名词的就去查,就去问同事,再不济就去论坛、交流群里问问,一开始是很困难了,没有计算机基础的她甚至连什么是C++,什么是Java,什么是Python都搞不懂
但当她慢慢地搞懂这些名词,开始慢慢的摸索属于自己的学习路线,从计算机基础知识开始,一直到自动化测试
计算机基础知识>>>Python编程语言>>>数据库知识>>>WEBUI自动化测试>>>APP自动化测试>>>接口自动化测试>>>pytest>>>jenkins>>>PageObject设计模式
分享一些她的学习笔记,大致的学习路线就像这样,完整的图有点大,截全图就太糊了,不过需要这份学习路线完整图的伙伴,可以点击并输入暗号:CSDN进行领取
黄某在入行的时候,就见过互联行业里的一句话“如果你写过10000行不重复的有效代码,你就算入门互联网前沿了。”
经过了将近八九个月零碎的学习,她觉得自己会了,但社会经验告诉过所有人一件事,你觉得自己会和你真的会是两码事,这两者之间是需要很多事情去磨炼的。
那怎么办呢?这个时候她想到了两种方法,github找开源项目和面试,虽然作为外包,但是每个月还是有三天假期可以请。
做开源项目——请假面试——做开源项目——请假面试
陆陆续续过了三四个月,她从外包公司这摊泥里脱离了出来,虽然样子好不狼狈,但是确实想着光
第二岗位是一家电商公司的,一家不大不小的公司,公司内部新开项目,正好缺人,招了一些大学应届生和社招跳槽的人
这里就告一段落了,我问了问当初她在面试时记忆最深刻的问题,毕竟她和我不一样,时间长了当初入行自动化测试行业的问题,我是真的不记得了
虽然说我也收集过不少朋友给的大厂面试真实题目,但隔着一个网络总有点不真实感,这些面试题我也进行过整理
需要领取这些上百次真实的大厂面试题可以点击并输入暗号:CSDN进行领取
webService接口:是走soap协议通过http传输,请求报文和返回报文都是xml格式的,我们在测试的时候都用通过工具才能进行调用,测试。可以使用的工具有SoapUI、jmeter、loadrunner等
http api接口:是走http协议,通过路径来区分调用的方法,请求报文都是key-value形式的,返回报文一般都是json串,有get和post等方法,这也是最常用的两种请求方式。可以使用的工具有postman、RESTClient、jmeter、loadrunner等
http协议又叫做超文本传输协议,在做网络请求的时候,我们基本上是使用http协议。
http协议包括请求和响应。
请求中包括:请求地址,请求方式,请求方式包括get请求和post请求,get和post的区别是get请求是在地址栏后边跟随请求参数,但是请求参数大小是有限制,不同浏览器是不同的。一般是4KB。post请求主要用于向服务器提交请求参数。post请求的参数是放到请求实体内容中的,相对get请求较为安全一些。
另外,请求中会有各种请求头信息,比如支持的数据类型,请求的来源位置,以及Cookie头等相关头信息。
响应,主要包含响应的状态码,像200(),404(),500(),304(),307()
还有各种响应头信息,比如设置缓存的响应头,Content-Type内容类型,设置cookie头信息。
首先这个题看似简单,实际上是个送命题!如果你百度搜到的标准答案可能是这
样的
简单的说:
GET 产生一个 TCP 数据包;POST 产生两个 TCP 数据包。
长的说:对于 GET 方式的请求,浏览器会把 http header 和 data 一并发送出去,服务器响应 200(返回数据); 而对于 POST,浏览器先发送 header,服务器响应 100 continue,浏览器再发送data,服务器响应 200 ok(返回数据)。
域名解析服务。将主机名转换为 IP 地址。如将 http://www.cnblogs.com/主机名转换为 IP 地址:211.137.51.78
已知一个字符串为“hello_world”, 如何得到一个队列
["hello","world"]
a = "hello_world"
b = a.split("_")
print(b)
已知一个数字为 1,如何输出“0001”
a = 1
print("%04d" % a)
已知一个队列,如: [1, 3, 5, 7], 如何把第一个数字,放到第三个位置,得到:
[3, 5, 1, 7]
a = [1, 3, 5, 7]
# insert 插入数据
a.insert(3, a[0])
print(a[1:])
打印出 100-999 所有的"水仙花数",所谓"水仙花数"是指一个三位数,其各位数
字立方和等于该数本身。例如:153 是一个"水仙花数",因为 153=1 的三次方+
5 的三次方+3 的三次方。
sxh = []
for i in range(100, 1000):
s = 0
m = list(str(i))
for j in m:
s += int(j)**len(m)
if i == s:
print(i)
sxh.append(i)
print("100-999 的水仙花数:%s" % sxh)
用 python 写个冒泡排序
a = [1, 3, 10, 9, 21, 35, 4, 6]
s = range(1, len(a))[::-1]
print(list(s)) # 交换次数
for i in s:
for j in range(i):
if a[j] > a[j + 1]:
a[j], a[j + 1] = a[j + 1], a[j]
print("第 %s 轮交换后数据:%s" % (len(s)-i+1, a))
print(a)
有 2 张表,学生表(student)基本信息如下
科目和分数表(grade)
select *
from grade
where kemu = '数学'
order by score
desc
limit 3
select a.id, a.name, b.kemu, b.score
from student a, grade b
where a.id = b.id
and kemu = '数学'
order by score
desc
limit 1, 2
select a.id, a.name, b.kemu, b.score
from student a, grade b
where a.id = b.id
and kemu = '数学'
order by score
desc
limit 3, 10000
SELECT b.kemu,
(SELECT COUNT(*) FROM grade WHERE score < 60 and kemu = b.kemu) as 不
及格,
(SELECT COUNT(*) FROM grade WHERE score between 60 and 80 and kemu = b.kemu)
as 一般,
(SELECT COUNT(*) FROM grade WHERE score > 80 and kemu = b.kemu) as 优 秀
FROM grade b
GROUP BY kemu
一般测试的项目里面,有个 logs 的目录文件,会存放日志文件,有个 xxx.out的文件,可以用 tail -f 动态实时查看后端日志
先 cd 到 logs 目录(里面有 xx.out 文件)
tail -f xx.out
这时屏幕上会动态实时显示当前的日志,ctr+c 停止
tail -1000 xx.out
find / -name tnsnames.ora
查到:
/opt/app/oracle/product/10.2/network/admin/tnsnames.ora
/opt/app/oracle/product/10.2/network/admin/samples/tnsnames.ora
还可以用 locate 来查找
locate tnsnames.ora
结果是:
/opt/app/oracle/product/10.2/hs/admin/tnsnames.ora.sample
/opt/app/oracle/product/10.2/network/admin/tnsnames.ora
/opt/app/oracle/product/10.2/network/admin/samples/tnsnames.ora
find / -name httpd.conf #在根目录下查找文件 httpd.conf,表示在整个硬盘查找
find /etc -name httpd.conf #在/etc 目录下文件 httpd.conf
find /etc -name ‘srm’ #使用通配符(0 或者任意多个)。表示在/etc 目录下查找文件名中含有字符串‘srm’的文件
find . -name ‘srm’ #表示当前目录下查找文件名开头是字符串‘srm’的文件
按照文件特征查找
find / -amin -10 # 查找在系统中最后 10 分钟访问的文件(access time)
find / -atime -2 # 查找在系统中最后 48 小时访问的文件
find / -empty # 查找在系统中为空的文件或者文件夹
find / -group cat # 查找在系统中属于 group 为 cat 的文件
find / -mmin -5 # 查找在系统中最后 5 分钟里修改过的文件(modify time)
find / -mtime -1 #查找在系统中最后 24 小时里修改过的文件
find / -user fred #查找在系统中属于 fred 这个用户的文件
find / -size +10000c #查找出大于 10000000 字节的文件(c:字节,w:双字,k:KB,M:MB,G:GB)
find / -size -1000k #查找出小于 1000KB 的文件
def is_element_exsist(driver, locator):
'''
判断元素是否存在,存在返回 True,不存返回 False
:param locator: locator 为元组类型,如("id", "yoyo")
:return: bool 值,True or False
'''
try:
driver.find_element(*locator)
return True
except Exception as msg:
print("元素%s 找不到:%s" % (locator, msg))
return False
if __name__ == '__main__':
loc1 = ("id", "yoyo") # 元素 1
print(is_element_exsist(driver, loc1))
# coding:utf-8
from selenium import webdriver
from PIL import Image
driver = webdriver.Chrome()
driver.get('http://www.baidu.com/')
driver.save_screenshot('button.png')
element = driver.find_element_by_id("su")
print(element.location) # 打印元素坐标
print(element.size) # 打印元素大小
left = element.location['x']
top = element.location['y']
right = element.location['x'] + element.size['width']
bottom = element.location['y'] + element.size['height']
im = Image.open('button.png')
im = im.crop((left, top, right, bottom))
im.save('button.png')
动态元素有 2 种情况,一个是属性动态,比如 id 是动态的,定位时候,那就不要用 id 定位就是了
<p id="cha" class="hello world">
<button id="cha_auto_20201210" name="heo" >登录</button>
<br>
</p>
比如上面这个 button 元素,id 是动态的,定位方法千千万,何必死在 id 上,可以用 name 定位,哪怕这个元素属性都是动态的,它的标签不可能动态吧,那就定位父元素
id="cha"a: .//*[@id=‘cha’]/button
还有一种情况动态的,那就是这个元素一会在页面上方,一会在下方,飘忽不定的动态元素,定位方法也是一样,按 f12,根据元素属性定位(元素的 tag、name的步伐属性是不会变的,动的只是 class 属性和 styles 属性)
他们的主要区别在于具体测试的细节和方法有区别,比如:性能测试,在 WEB测试只需要测试响应时间这个要素,在 App 测试中还需要考虑流量测试和耗电量测试。
兼容性测试:web主要兼容个浏览器,而app测试需要兼容不同品牌、分辨率、不同操作系统
安装测试:App 测试是存在客户端层面的安装测试,那么就具备相关的测试点。
交叉事件测试:在操作某个软件的时候,来电话、来短信,电量不足提示等外部事件。
操作类型测试:如横屏测试,手势测试
升级测试:升级测试的提醒机制,升级取消是否会影响原有功能的使用,升级后用户数据是否被清除了。
1.Android 长按 home 键呼出应用列表和切换应用,然后右滑则终止应用;
2.多分辨率测试,Android 端 20 多种,ios 较少;
3.手机操作系统,Android 较多,ios 较少且不能降级,只能单向升级;新的 ios系统中的资源库不能完全兼容低版本中的 ios 系统中的应用,低版本 ios 系统中的应用调用了新的资源库,会直接导致闪退(Crash);
4.操作习惯:Android,Back 键是否被重写,测试点击 Back 键后的反馈是否正确;应用数据从内存移动到 SD 卡后能否正常运行等;
5.push 测试:Android:点击 home 键,程序后台运行时,此时接收到 push,点击后唤醒应用,此时是否可以正确跳转;ios,点击 home 键关闭程序和屏幕锁屏的情况(红点的显示);
6.安装卸载测试:Android 的下载和安装的平台和工具和渠道比较多,ios 主要有 app store,iTunes 和 testflight 下载;
7.升级测试:可以被升级的必要条件:新旧版本具有相同的签名;新旧版本具有相同的包名;有一个标示符区分新旧版本(如版本号),对于 Android 若有内置的应用需检查升级之后内置文件是否匹配(如内置的输入法)
总结有以下两点:
1.主线程执行了耗时操作,比如数据库操作或网络编程
2.其他进程(就是其他程序)占用 CPU 导致本进程得不到 CPU 时间片,比如其他进程的频繁读写操作可能会导致这个问题。
主要有如下几点:
抓日志
1. app 开发保存错误日志到本地
一般 app 开发在 debug 版本,出现 anr 和 crash 的时候会自动把日志保存到本地实际的 sd 卡上,去对应的 app 目录取出来就可以了
2.第三方 sdk 统计工具
一般接入了第三方统计 sdk,比如友盟统计,在友盟的后台会抓到报错的日志
常见的异常几种如下:
postman和jmeter
webService 接口用 SoapUI
用一个全局变量来处理依赖的数据,比如登录后返回 token,其它接口都需要这个 token,那就用全局变量来传 token 参数
1.抓包,用 fiddler 工具抓包,或者浏览器上 f12,app 上的话,那就用 fiddler设置代理,去看请求报文和返回报文了
2.查看后端日志,xhell 连上服务器,查看日志
json 本质上是字符串,只是按 key:value 这种键值对的格式来的字符串
import json
# a 是字典 dict
a = {
"a": 1, "b": 2, "c": True}
# b 是 json
b = '{"a": 1, "b": 2, "c": true}'
print(type(a))
print(json.dumps(a)) # a 转 json
运行结果
<class 'dict'>
{
"a": 1, "b": 2, "c": true}
<class 'str'>
{
'a': 1, 'b': 2, 'c': True}
1.对于账号密码,这种管全局的参数,可以用命令行参数,单独抽出来,写的配置文件里(如 ini)
2.对于一些一次性消耗的数据,比如注册,每次注册不一样的数,可以用随机函数生成
3.对于一个接口有多组测试的参数,可以参数化,数据放yaml,text,json,excel都可以
4.对于可以反复使用的数据,比如订单的各种状态需要造数据的情况,可以放到数据库,每次数据初始化,用完后再清理
5.对于邮箱配置的一些参数,可以用 ini 配置文件
6.对于全部是独立的接口项目,可以用数据驱动方式,用 excel/csv 管理测试的接口数据
7.对于少量的静态数据,比如一个接口的测试数据,也就 2-3 组,可以写到 py脚本的开头,十年八年都不会变更的
参数化就是代码用例写好了后,不需要改代码,只需维护测试数据就可以了,并且根据不同的测试数据生成多个用例
数据驱动就像
import unittest
import ddt
# 测试数据
datas = [ {
"user": "admin", "psw": "123", "result": "true"},
{
"user": "admin1", "psw": "1234", "result": "true"},
{
"user": "admin2", "psw": "1234", "result": "true"},
{
"user": "admin3", "psw": "1234", "result": "true"},
{
"user": "admin4", "psw": "1234", "result": "true"},
{
"user": "admin5", "psw": "1234", "result": "true"},
{
"user": "admin6", "psw": "1234", "result": "true"},
{
"user": "admin7", "psw": "1234", "result": "true"},
{
"user": "admin8", "psw": "1234", "result": "true"},
{
"user": "admin9", "psw": "1234", "result": "true"},
{
"user": "admin10", "psw": "1234", "result": "true"},
{
"user": "admin11", "psw": "1234", "result": "true"}]
@ddt.ddt
class Test(unittest.TestCase):
@ddt.data(*datas)
def test_(self, d):
"""cha:{0}"""
她的笔记有点多,我看的时候,好家伙word文档一千多页,此外她还记在了笔记本上,写了有一本半的笔记,铺的满满当当
我就翻看了一下,挑了些比较符合面试官会问的问题,当然通过朋友(py)交易,将那份笔记弄到了手,有需要的可以点击并输入暗号:CSDN进行领取