前言
提前声明,这只是我个人求职面试的一些总结和积累,它并不适合所有人。
测试
什么是单元测试、功能测试、集成测试?
1.单元测试
是对软件组成单元进行测试。其目的是检验软件基本组成单位的正确性。测试的对象是软件设计的最小单位:模块。又称为模块测试。
2.集成测试
也称联合测试(联调)、组装测试,将程序模块采用适当的集成策略组装起来,对系统的接口及集成后的功能进行正确性检测的测试工作。集成主要目的是检查软件单位之间的接口是否正确。
3.功能测试
检查特定的功能是否正常。功能测试不关心中间结果或者副作用,只关注最终结果。
什么是兼容性测试?
兼容性测试
主要是检查软硬件平台上是否可以正常运行,即通常说的软件的可移植性,细分的话,有平台兼容,网络兼容,数据库兼容,数据格式的兼容等等。
讲一下集成测试和单元测试的区别
1.颗粒度不同
:集成测试粒度居中,单元测试粒度最小,系统测试粒度最大。
2.测试方式不同
:集成测试一般由开发小组采用白盒加黑盒的方式来测试,单元测试一般由开发小组采用白盒方式来测试,系统测试一般由独立测试小组采用黑盒方式来测试。
3.测试内容不同
:集成测试既验证“设计”,又验证“需求”,单元测试主要测试单元是否符合“设计”,系统测试主要测试系统是否符合“需求规格说明书”。
4.使用阶段不同
:单元测试为开发人员在开发阶段要做的事情,集成测试和系统测试为测试人员在测试周期内做的工作。
边界值和等价类的区别
等价类划分
属于黑盒测试,它将不能穷举的测试过程进行分类,从而保证完整性和代表性。
它从以下方面思考:
1.确定有效等价类和无效等价类
2.有效等价类划分
(题目条件,还要注意边界值(极值),中间再随意找个值)
3.无效等价类划分
(跟有效等价类相反,其它特殊情况(中文、英文、特殊符号、空格、空))
边界值分析法
也属于黑盒测试,因为在程序中边界值最容易出现问题,因此找到边界值和它两端的值,分别进行测试。
边界值思路应该是选到边界和刚超过的值,来进行测试,也要根据实际情况来选择;边界值和等价类是相辅相成的关系,配合使用的。
知道冒烟测试么
冒烟测试
这一术语源自硬件行业,它的主要目的是快速验证软件基本功能是否有缺陷
。
如果冒烟测试的测试例不能通过,则不必做进一步的测试。进行冒烟测试之前需要确定冒烟测试的用例集,对用例集要求覆盖软件的基本功能。这种版本包出包之后的验证方法通常称为软件版本包的门槛用例验证
。
冒烟测试的目的就是先通过最基本的测试,如果最基本的测试都有问题,就直接打回开发部门,减少测试部门时间的浪费。
冒烟测试可以手动执行,也可以自动化执行。稳定的系统适合自动化冒烟测试,集成过程中的系统适合手工冒烟测试
,因为冒烟测试内容在动态变化,变化中的自动化脚本维护工作量比较大。
问做过哪些测试相关的工作, 后续就具体参与的测试内容进行提问
详述做过哪些方面的测试以及是如何做的,也可以讲述整个测试流程。
说一下黑盒测试的测试方法
黑盒测试也称功能测试
,它是通过测试来检测每个功能是否都能正常使用。
在测试中,把程序看作一个不能打开的黑盒子,在完全不考虑程序内部结构和内部特性的情况下,在程序接口进行测试,它只检查程序功能是否按照需求规格说明书的规定正常使用,程序是否能适当地接收输入数据而产生正确的输出信息。
黑盒测试着眼于程序外部结构,不考虑内部逻辑结构,主要针对软件界面和软件功能进行测试。
黑盒测试是以用户的角度,从输入数据与输出数据的对应关系出发进行测试的。
黑盒测试
的测试方法有:等价类划分
、边界值分析法
、猜错法
、随机数法
、因果图
。
等价类划分法
:把所有可能的输入数据划分成若干子集,然后从每一个子集中选取少数具有代表性的数据作为测试用例。
边界值分析法
:边界条件就是软件操作界限所在的边缘条件。经实践总结:大量的软件缺陷发生在输入域的边界上。所以在设计测试用例的时候,应该重视边界。同时它也是“等价类划分”这种测试方法的良好补充。
性能测试包括哪些方面, 性能测试我们关注的指标有哪些?
性能测试
一般情况下会关注并发数
、响应时间
、吞吐量
、错误率
以及资源利用率
(比如CPU、Mem、IO、Network)等方面,具体可参考下面的博客:
性能测试报告包含哪些关键的性能指标?
python 基础
说一下函数参数里的 *args and **kwargs
首先用*args
和**kwargs
只是为了方便并没有强制使用它们。
当你不确定你的函数里将要传递多少参数时你可以用*args
。例如,它可以传递任意数量的参数:
>>> def print_everything(*args):
for index, value in enumerate(args):
print(f'{index}. {value}')
...
>>> print_everything('apple', 'banana', 'cabbage')
0. apple
1. banana
2. cabbage
相似的,**kwargs
允许你使用没有事先定义的参数名:
>>> def table_things(**kwargs):
for key, value in kwargs.items():
print(f'{key} = {value}')
...
>>> table_things(apple='fruit', cabbage='vegetable')
apple = fruit
cabbage = vegetable
当调用函数时你也可以用*
和**
语法。例如:
>>> def print_everything(*args):
for index, value in enumerate(args):
print(f'{index}. {value}')
...
>>> fruits_list = ['apple', 'banana', 'pear']
>>> print_everything(*fruits_list)
0. apple
1. banana
2. pear
>>> def table_things(**kwargs):
for key, value in kwargs.items():
print(f'{key} = {value}')
...
>>> things_dict = {'fruit': 'apple', 'vegetable': 'cabbage'}
>>> table_things(**things_dict)
fruit = apple
vegetable = cabbage
就像你看到的一样,它可以传递列表(或者元组)和字典并把它们解包。
*args
和**kwargs
可以同时出现在函数的定义中,但是*args
必须在**kwargs
前面。
>>> def print_everything(*args, **kwargs):
for index, value in enumerate(args):
print(f'{index}. {value}')
for key, value in kwargs.items():
print(f'{key} = {value}')
...
>>> print_everything('apple', 'banana', fruit='pear', vegetable='cabbage')
0. apple
1. banana
fruit = pear
vegetable = cabbage
*args
和**kwargs
同时出现的形式多见于子类的__init__
方法里:
class Foo(object):
def __init__(self, value1, value2):
print(value1, value2)
class MyFoo(Foo):
def __init__(self, *args, **kwargs):
print('myfoo')
value1 = args[1]
value2 = kwargs['value2']
super().__init__(value1, value2)
myfoo = MyFoo(0, 'on', value2='off')
说一下实例方法、静态方法和类方法
通过一个例子进行简单说明三者的概念与区别:
GLOBAL_DATA = 'global'
class A:
class_data = 0
def __init__(self, data):
self.instance_data = data
def foo(self):
'''实例方法'''
print("executing foo, instance_data is: {}".format(self.instance_data))
@classmethod
def class_foo(cls):
'''类方法'''
print("executing class_foo, class_data is :{}".format(cls.class_data))
@staticmethod
def static_foo():
'''静态方法'''
print("executing static_foo, global_data is :{}".format(GLOBAL_DATA))
a = A('test')
# 实例方法
a.foo() # executing foo, instance_data is: test
# 类方法
a.class_foo() # executing class_foo, class_data is :0
A.class_foo() # executing class_foo, class_data is :0
# 静态方法
A.static_foo() # executing static_foo, global_data is :global
a.static_foo() # executing static_foo, global_data is :global
实例方法
是Python
中用得最多的方法,它第一个参数必须是实例对象
,该参数名一般约定为self
,通过它来传递实例的属性和方法
(也可以传递类的属性和方法
)。如上例中的foo
方法,当通过a.foo()
调用实例变量时,会将a
传递给self
参数,相当于执行foo(a)
。
需要注意的是:不能直接使用A.foo()
调用实例方法。换句话说不能直接用类调用实例方法,只能通过实例对象去调用实例方法。
类方法
采用装饰器@classmethod
来定义,第一个参数必须是当前类对象
,该参数名一般约定为cls
,通过它来传递类的属性和方法
(不能传递实例的属性和方法
)。上例中的class_foo
方法就是一个类方法,当A.class_foo()
调用类方法时,会将类传递给cls
,相当于执行class_foo(A)
。
再强调一下Python
中的重要概念:一切皆是对象
,类也是一个对象!
对于类方法
,既可以通过类对象
也可以通过实例对象
来调用。
静态方法
使用装饰器@staticmethod
来定义,参数没有限制,但是在方法体中不能使用类或实例的任何属性和方法
,如上例中的static_foo
方法。它主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系
。
如何快速实现列表元素去重并保留原始顺序
如何快速找出一个列表中出现次数最多的元素
请说一下Python里的super是什么,它有什么用
super()
函数是用于调用父类(超类)的一个方法。
super()
是用来解决多重继承
问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO
)、重复调用等种种问题。
MRO
就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
我们在学习 Python 类
的时候,总会碰见书上的类中有 __init__()
这样一个函数,很多同学百思不得其解,其实它就是 Python 的构造方法
。
构造方法类似于init()
这种初始化方法,来初始化新创建对象的状态,在一个对象被创建以后会立即调用,比如像实例化一个类:
f = FooBar()
f.init()
使用构造方法就能让它简化成如下形式:
f = FooBar()
你可能还没理解到底什么是构造方法,什么是初始化,下面我们再来举个例子:
class FooBar:
def __init__(self):
self.somevar = 42
>>>f = FooBar()
>>>f.somevar
>>>42
我们会发现在实例化 FooBar
后,实例对象
直接就能够访问somevar
的值;如果说没有用构造方法初始化,就不能够调用,明白了吗?
其实你可以这么理解,f = FooBar()
是实例化FooBar
类并创建了一个实例对象 f
,该过程会去自动调用__init__
方法,帮我们给实例对象 f
绑定一些属性,比如somevar
。
在明白了构造方法之后,我们来点进阶的问题,那就是构造方法中的初始值无法继承的问题。
例子:
class Bird:
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry:
print('Ahahahah')
else:
print('No thanks!')
class SongBird(Bird):
def __init__(self):
self.sound = 'Squawk'
def sing(self):
print(self.sound)
sb = SongBird()
sb.sing() # 能正常输出
sb.eat() # 报错,因为 SongBird中没有 hungry 特性
Traceback (most recent call last):
File "G:/python/demo.py", line 22, in
sb.eat() # 报错,因为 SongBird 中没有 hungry 特性
File "G:/python/demo.py", line 6, in eat
if self.hungry:
AttributeError: 'SongBird' object has no attribute 'hungry'
Squawk
进程已结束,退出代码为 1
从报错信息来看,很明显子类实例化后的实例对象sb
是可以调用父类的eat
方法的,否则报错信息就是 AttributeError: 'sb' object has no method 'eat'
。但实际提示hungry
属性未找到。
那解决这个问题的办法有两种:
1、调用未绑定的超类构造方法(多用于旧版 python 阵营)
class SongBird(Bird):
def __init__(self):
Bird.__init__(self)
self.sound = 'Squawk'
def sing(self):
print(self.sound)
2、使用super函数(推荐使用)
class SongBird(Bird):
def __init__(self):
super(SongBird, self).__init__()
self.sound = 'Squawk'
def sing(self):
print(self.sound)
不过Python3
里super().__init__()
可以代替super(SongBird, self).__init__()
这种写法,并且更推荐此写法:
class SongBird(Bird):
def __init__(self):
super().__init__()
self.sound = 'Squawk'
def sing(self):
print(self.sound)
原理:它会查找所有的超类
,以及超类的超类
,直到找到所需的属性或方法
为止。
正则表达式了解么
需要回答 元字符
、预定义匹配字符集
、重复匹配
、位置匹配
、贪婪非贪婪匹配
和分组匹配
,紧接着介绍Python
下是怎么使用正则的,其实就是re
模块三大方法:match
、search
和findall
。
详情可参考之前写的博客:正则表达式 。
说一下Python的多线程,什么是GIL锁,协程了解过么
同步、异步以及协程请参考 第11关 - 建立爬虫军队 ,多线程请参考 Python 多线程 。
线程全局锁
(Global Interpreter Lock),即Python
为了保证线程安全
而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程
。对于IO密集型任务
,Python
的多线程起到作用,但对于CPU密集型任务
,Python
的多线程几乎占不到任何优势,还有可能因为争夺资源而变慢。
解决办法就是多进程和多协程(协程也只是单CPU,但是能减小切换代价提升性能).
见 Python 最难的问题 ,这篇文章不用了解这么深。
简单点说协程是进程和线程的升级版,进程和线程都面临着内核态和用户态的切换问题而耗费许多切换时间,而协程就是用户自己控制切换的时机,不再需要陷入系统的内核态。
接口测试
什么是接口测试?
接口测试
是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间
以及内部各个子系统之间
的交互点。测试的重点是要检查数据的交换,传递和控制管理过程
,以及系统间的相互逻辑依赖关系
等。
简单来说就是通过URL向服务器或者其他模块等,传输我们想传输的数据,然后看看它们返回的是不是我们预期想要的。
为什么要做接口测试?
1.越底层发现Bug
,它的修复成本是越低的。
2.前端随便变,接口测好了,后端不用变,前后端是两拨人开发的。
3.检查系统的安全性、稳定性,前端传参不可信。
4.如今的系统复杂度不断上升,传统的测试方法成本急剧增加且测试效率大幅下降,接口测试可以提供这种情况下的解决方案。
5.接口测试相对容易实现自动化持续集成,且相对UI
自动化也比较稳定。
6.现在很多系统前后端架构是分离的,从安全层面来说:
- 只依赖前端进行限制已经完全不能满足系统的安全要求, 需要后端同样进行控制,在这种情况下就需要从接口层面进行验证。
- 前后端传输、日志打印等信息是否加密传输也是需要验证的,特别是涉及到用户的隐私信息。
接口测试的工具有哪些?
项目前后端调用主要是基于http
协议的接口,所以测试接口时主要是通过工具或代码模拟发送http
请求。工具有很多如:postman
和jmeter
等。也可以用接口自动化来实现,就是用代码实现,比如Python + requests
。
接口测试的流程是什么?
后端接口测试的测试点以及测试活动内容方面回答,见思维导图:
接口测试质量评估标准
1.业务功能覆盖是否完整
2.参数验证是否达到要求(边界、业务规则)
3.接口异常场景覆盖是否完整
4.代码覆盖率是否达到要求
5.性能指标是否满足要求
6.安全指标是否满足要求
请问你在上家公司是如何做接口测试的?
把你做过的内容以及流程描述清楚即可。
您在以往的测试工作中都曾经具体从事过哪些工作?其中最擅长哪部分工作?
功能测试、可靠性测试、性能测试以及自动化测试,具体擅长什么就回答什么。
说一下Get和Post请求有什么区别
1.get
使用URL
或Cookie
传参。而post
将数据放在body
中。
2.get
的URL
会有长度上的限制,则post
的数据则可以非常大。
3.post
比get
安全,因为数据在地址栏上不可见。
4.一般get
请求用来获取数据,post
请求用来发送数据。
其实上面这几点,只有最后一点说的是比较靠谱的,第一点post
请求也可以把数据放到url
里面,get
请求其实也没长度限制,post
请求看起来参数是隐式的,稍微安全那么一些些,但是那只是对于小白用户来说的,就算post
请求,你通过抓包也是可以抓到参数的。(唯一区别就是最后一点,上面3点区别都是不准确的)
http状态码
200
2
开头的都表示这个请求发送成功,最常见的就是200
,就代表这个请求是ok的,服务器也返回了。
300
3
开头的代表重定向,最常见的是302
,把这个请求重定向到别的地方了。
400
代表客户端发送的请求有语法错误
401
代表访问的页面没有授权
403
表示没有权限访问这个页面
404
代表没有这个页面。
500
5
开头的代表服务器有异常
500
代表服务器内部异常
504
代表服务器端超时,没返回结果。
web端测试
cookie和session的区别?
cookie
数据存放在客户的浏览器上,session
数据放在服务器上。cookie
不是很安全,别人可以分析存放在本地的cookie
并进行cookie
欺骗。考虑到安全应当使用session
。session
会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能。考虑到减轻服务器性能方面,应当使用cookie
。- 单个
cookie
保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie
。
Linux 命令
常用的Linux命令有哪些,动态查看日志的命令是?
1.查看当前打印日志,进行实时打印时使用 tail -f 文件名
2.日志文件不大,可使用cat 文件名
查看文件内容
3.当查询指定内容,且不需要查看内容的上下文时使用grep -n 关键词 文件名
4.vi/vim
查看,q!
不保存退出
怎么找到占用内存最多的前5个进程
ps -aux | sort -k4nr | head -5
ps
参数-aux
:
a
显示所有程序
u
以用户为主的格式来显示程序状况
x
显示所有程序,不以终端机来区分
显示效果:
sort
里的 -k
表示按照哪一列去排序,后面跟的数字4
表示根据第四列内存使用率进行排序,如果写成 -k3
表示根据第三列CPU
使用率排序,-n
表示按照数值大小进行排序,-r
选项表示降序,是reverse
的缩写,默认排序是升序,也就是从小到大,加上-r
从大到小降序排序。
head
:-N
可以指定显示的行数,默认显示10行。
一个文件里面以英文问号分割,怎么取出第三句
cat demo.txt | awk -F '?' '{print $3}'
linux中授予文件访问权限的命令是什么?chmod 664是什么含义?
Linux系统上对文件的权限有着严格的控制。
Linux下文件的权限类型一般包括读
,写
,执行
,对应字母为 r
、w
、x
。
Linux下权限的粒度有 拥有者
、群组
、其它组
三种,对应user
、group
和other
,每个文件都可以针对三个粒度,设置不同的rwx(读写执行)
权限。
Linux上通常使用 chmod
对文件的权限进行设置和更改,数字 4
、2
和1
分别表示读
、写
、执行
权限,即 r=4
,w=2
,x=1
。
rwx
(4 + 2 + 1 = 7)表示可读可写可执行
rw-
(4 + 2 = 6)表示可读可写不可执行
r-x
(4 + 1 = 5)表示可读可执行不可写
chmod 664 file
等价于 chmod u=rw-,g=rw-,o=r-x file
十位权限表示法:
-rw------- (600) 只有拥有者有读写权限。
-rw-r--r-- (644) 只有拥有者有读写权限;而属组用户和其他用户只有读权限。
-rwx------ (700) 只有拥有者有读、写、执行权限。
-rwxr-xr-x (755) 拥有者有读、写、执行权限;而属组用户和其他用户只有读、执行权限。
-rwx--x--x (711) 拥有者有读、写、执行权限;而属组用户和其他用户只有执行权限。
-rw-rw-r-- (664) 拥有者和属组用户有读写权限;而其他用户只有读权限。
-rw-rw-rw- (666) 所有用户都有文件读、写权限。
-rwxrwxrwx (777) 所有用户都有读、写、执行权限。
十位权限里的第一位代表文件类型:
d代表的是目录(directroy)
-代表的是文件(regular file)
s代表的是套字文件(socket)
p代表的管道文件(pipe)或命名管道文件(named pipe)
l代表的是符号链接文件(symbolic link)
b代表的是该文件是面向块的设备文件(block-oriented device file)
c代表的是该文件是面向字符的设备文件(charcter-oriented device file)
个人
为什么选择做测试?
觉得自己做测试的优势是什么
你是如何看待加班的?
未来的职业规划
这一部分内容你自由发挥就好。