基于功能性需求和非功能性需求思考:
功能性需求使用等价类划分、边界值分析、错误推断法设计用例
非功能性需求考虑安全(信息的保存、传输是否加密,被gj,bl破解)、性能(多快好省,响应时间,tps,资源占用)、兼容性(同一浏览器不同版本,不同浏览器,不同移动端,不同屏幕大小,不同分辨率)
等价类划分、边界值分析、错误推测法(根据个人经验推测可能出现的错误)
bug漫游:一个bug出生地漫游到其他提供相同功能地方,验证是否存在相同的bug
1)深入理解被测软件的架构,发现系统边界以及系统集成的潜在缺陷
*数据库连接方式、数据库的读写分离、消息中间件 Kafka 的配置、缓存系统的层级分布、第三方系统的集成*
2)理解被测软件的设计与实现细节,深入理解软件内部的处理逻辑
3)引入需求覆盖率和代码覆盖率来衡量测试执行的完备性,并以此为依据找出遗漏的测试点
单元测试是指,对软件中的最小可测试单元在于程序其他部分相隔离的情况下进行检查和验证的工作,这里的最小可测试单元通常指函数或者类。
第一、代码的基本特征与产生错误的原因
要做到代码功能逻辑正确,必须做到分类正确且完备无遗漏,同时每分类的处理逻辑必须正确。
第二、单元测试用例详解
单元测试的用例是一个“输入数据”和“预计输出”的集合。
输入数据:被测函数的输入参数、需要读取的全局静态变量、成员变量、调用子函数获得的数据、调用子函数改写的数据
预计输出:被测函数的返回值、输出参数,改写的成员变量、全局变量,更新的文件、数据库、消息队列
第三、驱动代码、桩代码和mock代码
驱动代码是用来调用被测函数的,而桩代码和 Mock 代码是用来代替被测函数调用的真实代码的。
驱动代码(Driver)指调用被测函数的代码
桩代码(Stub)是用来代替真实代码的临时代码。桩代码的应用首先起到了隔离和补齐的作用,使被测代码能够独立编译、链接,并独立运行。同时,桩代码还具有控制被测函数执行路径的作用。
Mock 代码和桩代码的本质区别是:测试期待结果的验证(Assert and Expectiation)。对于 Mock 代码来说,我们的关注点是 Mock 方法有没有被调用,以什么样的参数被调用,被调用的次数,以及多个 Mock 函数的先后调用顺序。所以,在使用 Mock 代码的测试中,对于结果的验证(也就是 assert),通常出现在 Mock 函数中。对于桩代码来说,我们的关注点是利用 Stub 来控制被测函数的执行路径,不会去关注 Stub 是否被调用以及怎么样被调用。所以,你在使用 Stub 的测试中,对于结果的验证(也就是 assert),通常出现在驱动代码中。
自动化测试可以把人力从机械重复的测试工作中解脱出来,将更多精力放在新功能的测试和更全面的测试用例设计上,所以自动化测试技术是为业务测试服务,要将更多精力放在业务测试上才对。
代码级集成测试与单元测试最大的区别只是,代码级集成测试中被测函数内部调用的其他函数必须是真实的,不允许使用桩代码代替,而单元测试中允许使用桩代码来模拟内部调用的其他函数。
java :REST Assured。参考资料:REST-Assured,接口自动化的 “瑞士军刀“- 初识篇_rest-assured pom_软件测试狂阿沐的博客-CSDN博客
python : pytest、httprunner
因为通常不会对response中的所有字段进行assert,所以Response 验证自动化的核心思想是自动比较两次相同 API 调用的返回结果,并自动识别出有差异的字段值,比较过程可以通过规则配置去掉诸如时间戳、会话 ID(Session ID)等动态值。
高的代码覆盖率不一定能保证软件的质量,但是低的代码覆盖率一定不能能保证软件的质量。
JaCoCo,一款 Java 代码的主流开源覆盖率工具,可以很方便地嵌入到 Ant、Maven 中,并且和很多主流的持续集成工具以及代码静态检查工具,比如 Jekins 和 Sonar 等。
最基本的方法就是注入,在被测代码中自动插入用于覆盖率统计的探针(Probe)代码,并保证插入的探针代码不会给原代码带来任何影响。
字节码注入可分为On-The-Fly 注入模式和Offline 注入模式。
主流的 JaCoCo采用的java Agent方式。
缺陷报告本身的质量将直接关系到缺陷被修复的速度以及开发工程师的效率,同时还会影响到测试工程师的信用、测试与开发人员协作的有效性。
一份高效的软件缺陷报告,应该包括缺陷标题、缺陷概述、缺陷影响、环境配置、前置条件、缺陷重现步骤、期望结果和实际结果、优先级和严重程度、变通方案、缺陷发现版本、出现频率、根原因分析,以及附件这几大部分。
测试计划包括:测试范围、测试策略、测试资源、测试进度和测试风险预估
1)测试范围:被测对象以及主要的测试内容,明确“测什么”和“不测什么”
2)测试策略:明确“先测什么后测什么”和“如何来测”,还需要说明,采用什么样的测试类型和测试方法
功能测试
兼容性测试
性能测试
易用性测试
UI测试
安全测试
3)测试资源:通常包括测试人员和测试环境。明确“谁来测”和“在哪里测”这两个问题。
4)测试进度:描述各类测试的开始时间,所需工作量,预计完成时间, 并以此为依据来建议最终产品的上线发布时间。
5)测试风险预估:通常需求变更、开发延期、发现重大缺陷和人员变动是引入项目测试风险的主要原因。
1)兼容性测试
如何确定需要覆盖的移动设备类型以及移动端版本列表?
如果是既有产品,你可以通过大数据技术分析产品的历史数据得出 Top 30% 的移动设备以及 iOS/Android 的版本列表,那么兼容性测试只需覆盖这部分即可。如果是一个全新的产品,你可以通过 TalkingData 这样的网站来查看目前主流的移动设备,分辨率大小、iOS/Android 版本等信息来确定测试范围。
2)性能测试
需要在明确了性能需求(并发用户数、响应时间、事务吞吐量等)的前提下,结合被测系统的特点,设计性能测试场景并确定性能测试框架。eg,是直接在 API 级别发起压力测试,还是必须模拟终端用户行为进行基于协议的压力测试。再比如,是基于模块进行压力测试,还是发起全链路压测。
测试策略设计能力是指,对于各种不同的被测软件,能够快速准确地理解需求,并在有限的时间和资源下,明确测试重点以及最适合的测试方法的能力。
具备出色的测试策略设计能力,你可以非常明确地回答出测试过程中遇到的这些关键问题:
学习能力,当你学习一个新的开源工具时,建议你直接看官方文档:一来,这里的内容是最新而且是最权威的;二来,可以避免网上信息质量的参差不齐。知识输入源头是单一,而且权威的话,你的学习曲线也必然会比较平滑。
缺陷分析能力,通常包含三个层面的含义:
1)对于已经发现的缺陷,结合发生错误的上下文以及后台日志,可以预测或者定位缺陷的发生原因,甚至可以明确指出具体出错的代码行,由此可以大幅缩短缺陷的修复周期,并提高开发工程师对于测试工程师的认可以及信任度;
2)根据已经发现的缺陷,结合探索性测试思维,推断同类缺陷存在的可能性,并由此找出所有相关的潜在缺陷;
3)可以对一段时间内所发生的缺陷类型和趋势进行合理分析,由点到面预估整体质量的健康状态,并能够对高频缺陷类型提供系统性的发现和预防措施,并以此来调整后续的测试策略。
自动化测试的核心价值还是“测试”本身,“自动化”仅仅是手段,实际工作中千万不要本末倒置,把大量的精力放在“自动化”上,一味追求自动化而把本质的“测试”弱化了。
沟通能力会直接影响事务开展的效率。良好清晰的沟通能力,是一个技术优秀的测试工程师能否获得更大发展的“敲门砖”,也是资深测试工程师或者测试主管的核心竞争力。
首先既然是测试开发工程师,那么代码开发能力是最基本的要求。可以说,一个合格的测试开发工程师一定可以成为一个合格的开发工程师,但是一个合格的开发工程师不一定可以成为合格的测试开发工程师
除了代码开发能力,测试开发工程师更要具备测试系统需求分析的能力。你要能够站在测试架构师的高度,识别出测试基础架构的需求和提高效率的应用场景。从这个角度说,你更像个产品经理,只不过你这个产品是为了软件测试服务的。
测试开发工程师需要具备非常宽广的知识体系,你不仅需要和传统的测试开发工程师打交道,因为他们是你构建的测试工具或者平台的用户;而且还要和 CI/CD、和运维工程师们有紧密的联系,因为你构建的测试工具或者平台,需要接入到 CI/CD 的流水线以及运维的监控系统中去。
除此之外,你还要了解更高级别的测试架构部署和生产架构部署、你还必须对开发采用的各种技术非常熟悉。可见,对于测试开发工程师的核心竞争力要求是非常高的,这也就是为什么现今市场上资深的测试开发工程师的价格会高于资深的开发工程师的原因。
小到Linux/Unix/Windows操作系统的基础知识,Oracle/MySQL等传统关系型数据库技术,NoSQL非关系型数据库技术,中间件技术,Shell/Python脚本开发,版本管理工具与策略,CI/cd流水线设计,F5负载均衡技术,Fiddler/Wireshark/Tcpdump等抓包工具,浏览器Developer Tool等;
大到网站架构设计,容器技术,微服务架构,服务网格(Service Mesh),DevOps,云计算,大数据,人工智能和区块链技术等。
DevOps 的具体表现形式可以是工具、方法和流水线,但其更深层次的内涵还是在思想方法,以敏捷和精益为核心,通过发现问题,以系统性的方法或者工具来解决问题,从而实现持续改进。
对于 DevOps,可以从深入掌握 Jenkins 之类的工具开始,到熟练应用和组合各种 plugin 来完成灵活高效的流水线搭建,之后再将更多的工具逐渐集成到流水线中以完成更多的任务。
1)单元测试
金字塔最底部是单元测试,属于白盒测试的范畴,通常由开发工程师自己完成。每个版本build过程中会反复执行单元测试。
2)API测试
金字塔中间部分是 API 测试,主要针对的是各模块暴露的接口,通常采用灰盒测试方法。灰盒测试方法是介于白盒测试和黑盒测试之间的一种测试技术,其核心思想是利用测试执行的代码覆盖率来指导测试用例的设计。
3)GUI测试
GUI 测试,也称为端到端(E2E,End-to-end)测试。模拟用户在软件界面上的各种操作,并验证这些操作对应的结果是否正确。缺点是用例维护的代价大。
互联网产品的 GUI 测试通常采用“手工为主,自动化为辅”的测试策略,手工测试往往利用探索性测试思想,针对新开发或者新修改的界面功能进行测试,而自动化测试的关注点主要放在相对稳定且核心业务的基本功能验证上。
互联网的测试重点在API上,API 测试可以实现良好的投入产出比。
互联网产品通常会分为应用层和后端服务,后端服务又可以进一步细分为应用服务和基础服务。互联网产品的全面单元测试只会应用在那些相对稳定和最核心的模块和服务上,而应用层或者上层业务服务很少会大规模开展单元测试。
Selenium WebDriver 是典型的 Server-Client 模式,Server 端就是 Remote Server。以下是 Selenium 2.0 工作原理的解析。
1、当使用 Selenium2.0 启动浏览器 Web Browser 时,后台会同时启动基于 WebDriver Wire 协议的 Web Service 作为 Selenium 的 Remote Server,并将其与浏览器绑定。绑定完成后,Remote Server 就开始监听 Client 端的操作请求。
2、执行测试时,测试用例会作为 Client 端,将需要执行的页面操作请求以 Http Request 的方式发送给 Remote Server。该 HTTP Request 的 body,是以 WebDriver Wire 协议规定的 JSON 格式来描述需要浏览器执行的具体操作。
3、Remote Server 接收到请求后,会对请求进行解析,并将解析结果发给 WebDriver,由 WebDriver 实际执行浏览器的操作。
4、WebDriver 可以看做是直接操作浏览器的原生组件(Native Component),所以搭建测试环境时,通常都需要先下载浏览器对应的 WebDriver。
“测试脚本和数据解耦”的本质是实现了数据驱动的测试,让操作相同但是数据不同的测试可以通过同一套自动化测试脚本来实现,只是在每次测试执行时提供不同的测试输入数据。
(1)POM模式将页面分成三层
(2)POM模式的核心要素(重点)
TestCase
继承 unittest.Testcase
类,并且依赖 page 类,从而实现相应的测试步骤。 1)把一些公共的方法放到此类中,这个类将被PO对象继承
"""
封装公共方法
"""
from selenium import webdriver
import time
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class Base:
def __init__(self, browser="chrome"):
"""
初始化driver
:param browser:浏览器名称
"""
if browser == "chrome":
self.driver = webdriver.Chrome()
elif browser == "firefox":
self.driver = webdriver.Firefox()
elif browser == "ie":
self.driver = webdriver.Ie()
else:
self.driver = None
print("请输入正确的浏览器,例如:chrome,firefox,ie")
def open_url(self, url):
"""
打开地址
:param url: 被测地址
:return:
"""
self.driver.get(url)
time.sleep(2)
def find_element(self, locator, timeout=10):
"""
定位单个元素,如果定位成功返回元素本身,如果失败,返回False
:param locator: 定位器,例如("id","id属性值")
:return: 元素本身
"""
try:
element = WebDriverWait(self.driver, timeout).until(EC.presence_of_element_located(locator))
return element
except:
print(f"{locator}元素没找到")
return False
def click(self, locator):
"""
点击元素
:return:
"""
element = self.find_element(locator)
element.click()
def send_keys(self, locator, text):
"""
元素输入
:param locator: 定位器
:param text: 输入内容
:return:
"""
element = self.find_element(locator)
element.clear()
element.send_keys(text)
def close(self):
"""
关闭浏览器
:return:
"""
time.sleep(2)
self.driver.quit()
if __name__ == '__main__':
base = Base()
base.open_url("https://sso.kuaidi100.com/sso/authorize.do")
base.close()
2)每个页面对应一个page类
"""
定位元素的定位器和操作元素方法分离开,元素定位器全部放一起,然后每一个操作元素动作写成一个方法。
管理登陆页面所有的元素,
以及操作这些元素所用的方法。
"""
from common.base import Base
class LoginPage(Base):
# 编写定位器和页面属性
name_input_locator = ("id", "name")
passwd_input_locator = ("id", "password")
submit_button_locator = ("id", "submit")
username = 'xxxxxxxxxxx'
userpasswd = 'xxxxxx'
url = 'https://sso.kuaidi100.com/sso/authorize.do'
# """封装元素操作"""
# 输入用户名
def name_imput(self):
self.send_keys(self.name_input_locator, self.username)
# 输入密码
def passwd_imput(self):
self.send_keys(self.passwd_input_locator, self.userpasswd)
# 点击登陆
def click_submit(self):
self.click(self.submit_button_locator)
if __name__ == '__main__':
base = Base('firefox')
base.open_url(url=LoginPage.url)
3)测试方法及测试类
# 1. 导入包
import unittest
from pages.login_page import LoginPage
# 定义测试类
class TestCaseLogin(unittest.TestCase):
def setUp(self) -> None:
self.driver = LoginPage()
self.driver.open_url(LoginPage.url)
def tearDown(self) -> None:
# 5. 关闭浏览器
self.driver.close()
#todo:添加数据驱动装饰器
def testLogin(self):
"""登陆测试用例"""
self.driver.name_imput()
self.driver.passwd_imput()
self.driver.click_submit()
if __name__ == '__main__':
unittest.main()
完成一个业务流程操作,往往会需要依次调用多个操作函数,但是操作函数和操作函数之间会有页面衔接的问题,即前序操作函数完成后的最后一个页面,必须是后续操作函数的第一个页面。如果连续的两个操作函数之间无法用页面衔接,那就需要在两个操作函数之间加入额外的页面跳转代码,或者是在操作函数内部加入特定的页面跳转代码。
业务流程抽象是,基于操作函数的更接近于实际业务的更高层次的抽象方式。基于业务流程抽象实现的测试用例往往具有较好的灵活性,可以根据实际测试需求方便地组装出各种测试用例。
业务流程的核心思想是,从业务的维度来指导测试业务流程的封装。由于业务流程封装通常很贴近实际业务,所以特别适用于组装面向终端用户的端到端(E2E)的系统功能测试用例,尤其适用于业务功能非常多,并且存在各种组合的 E2E 测试场景。
实际项目中,并不是所有的数据都可以通过 API 的方式实现创建和修改,很多数据的创建和修改直接在产品代码内完成,而且并没有对外暴露供测试使用的接口。直接操作数据库,创建或修改一条测试数据往往会涉及很多业务表,任何的遗漏都会造成测试数据的不准确,从而导致有些测试因为数据问题而无法进行。
两个思路:
对于相对稳定、很少有修改的数据,建议采用 Out-of-box 的方式,比如商品类目、厂商品牌、部分标准的卖家和买家账号等。
对于一次性使用、经常需要修改、状态经常变化的数据,建议使用 On-the-fly 的方式。
用 On-the-fly 方式创建测试数据时,上游数据的创建可以采用 Out-of-box 方式,以提高测试数据创建的效率。以订单数据为例,订单的创建可以采用 On-the-fly 方式,而与订单相关联的卖家、买家和商品信息可以使用 Out-of-box 方式创建。