[TOC]
1、首先,我们先来理一下思路。
```
正常的接口测试流程:
确定测试接口的工具 —> 配置需要的接口参数 —> 进行测试 —> 检查测试结果(有的需要数据库辅助)
—> 生成测试报告(html报告)
那么,我们就根据这样的过程来一步步搭建我们的框架。在这个过程中,我们需要做到业务和数据的分离,
这样才能灵活,达到我们写框架的目的。
接下来,我们来进行结构的划分。
```
![](https://img.kancloud.cn/9d/88/9d88b25940f403ccfe45859d152850b5_174x224.png)
```
common:存放一些共通的方法
result:执行过程中生成的文件夹,里面存放每次测试的结果
testCase:用于存放具体的测试case
testFile:存放测试过程中用到的文件,包括上传的文件,测试用例以及数据库的sql语句
caselist:txt文件,配置每次执行的case名称
config:配置一些常量,例如数据库的相关信息,接口的相关信息等
readConfig: 用于读取config配置文件中的内容
runAll:用于执行case
```
<br />
```
既然整体结构有了划分,接下来就该一步步的填充整个框架了,首先,我们先来看看config.ini和
readConfig.py两个文件,从他们入手。
```
```
# config.ini
[DATABASE]
host = 192.168.0.212
username = root
password = www.upsoft01.com
port = 3306
database = databasename
[HTTP]
# 接口的url
baseurl = http://192.168.0.212
port = 8000
timeout = 1.0
[EMAIL]
mail_host = smtp.163.com
mail_user = xxx@163.com
mail_pass = *********
mail_port = 25
sender = xxx@163.com
receiver = xxxx@qq.com/xxxx@qq.com
subject = python
content = "All interface test has been complited\nplease read the report file about the detile of result in the attachment."
testuser = Someone
on_off = 1
```
<br />
```
相信大家都知道这样的配置文件,没错,所有一成不变的东西,我们都可以放到这里来。
现在,我们已经做好了固定的“仓库”。来保存我们平时不动的东西,那么,我们要怎么把
它拿出来为我所用呢?这时候,readConfig.py文件出世了,它成功的帮我们解决了这个问题。
```
<details>
<summary>readConfig.py</summary>
```
# -*- coding: utf-8 -*-
import os
import codecs
import configparser
proDir = os.path.split(os.path.realpath(__file__))[0]
configPath = os.path.join(proDir, "config.ini")
class ReadConfig:
def __init__(self):
fd = open(configPath)
data = fd.read()
# remove BOM
if data[:3] == codecs.BOM_UTF8:
data = data[3:]
file = codecs.open(configPath, "w")
file.write(data)
file.close()
fd.close()
self.cf = configparser.ConfigParser()
self.cf.read(configPath)
def get_email(self, name):
value = self.cf.get("EMAIL", name)
return value
def get_http(self, name):
value = self.cf.get("HTTP", name)
return value
def get_db(self, name):
value = self.cf.get("DATABASE", name)
return value
```
</details>
<br />
我们定义的方法,根据名称取对应的值,当然了,这里我们只用到了get方法,还有其他的例如set方法,有兴趣的同学可以自己去探索下,也可以看看小编我自己的关于读取配置文件的博文[https://my.oschina.net/u/3041656/blog/793467](https://my.oschina.net/u/3041656/blog/793467),这里我们就不在累述了。
<br />
话不多说,我们先来看下common到底有哪些东西。
![](https://img.kancloud.cn/ca/f1/caf19c878c420592a3b6378a824d80ae_283x179.png)
```
既然配置文件和读取配置文件我们都已经完成了,也看到了common里的内容,接下来就
可以写common里的共通方法了,从哪个下手呢?今天,我们就来翻“Log.py”的牌
吧,因为它是比较独立的,我们单独跟他打交道,也为了以后它能为我们服务打下良好基础。
这里呢,我想跟大家多说两句,对于这个log文件呢,我给它单独启用了一个线程,这样在
整个运行过程中,我们在写log的时候也会比较方便,看名字大家也知道了,这里就是我们
对输出的日志的所有操作了,主要是对输出格式的规定,输出等级的定义以及其他一些输
出的定义等等。总之,你想对log做的任何事情,都可以放到这里来。我们来看下代码,没
有比这个更直接有效的了。
```
```
import logging
from datetime import datetime
import threading
```
首先,我们要像上面那样,引入需要的模块,才能进行接下来的操作。
```
# -*- coding: utf-8 -*-
import logging
import os
from datetime import datetime
import threading
from interfaceTest import readConfig
class Log:
def __init__(self):
global logPath, resultPath, proDir
proDir = readConfig.proDir
resultPath = os.path.join(proDir, "result")
# 创建result file 如果它不存在
if not os.path.exists(resultPath):
os.mkdir(resultPath)
# defined test result file name by localtime
logPath = os.path.join(resultPath, str(datetime.now().strftime("%Y%m%d%H%M%S")))
# create test result file if it doesn't exist
if not os.path.exists(logPath):
os.mkdir(logPath)
# defined logger
self.logger = logging.getLogger()
# defined log level
self.logger.setLevel(logging.INFO)
# defined handler
handler = logging.FileHandler(os.path.join(logPath, "output.log"))
# defined formatter
formatter = logging.Formatter('%(asctime)s - %(name)s -%(levelname)s - %(message)s')
# defined formatter
handler.setFormatter(formatter)
# add handler
self.logger.addHandler(handler)
```
```
现在,我们创建了上面的Log类,在__init__初始化方法中,我们进行了log的相关初始化
操作。具体的操作内容,注释已经写得很清楚了,这样,log的基本格式已经定义完成了,
至于其他的方法,就靠大家自己发挥了,毕竟每个人的需求也不同,我们就只写普遍的共用
方法啦。接下来,就是把它放进一个线程内了,请看下面的代码:
```
```
class MyLog:
log = None
mutex = threading.Lock()
def __init__(self):
pass
@staticmethod
def get_log():
if MyLog.log is None:
MyLog.mutex.acquire()
MyLog.log = Log()
MyLog.mutex.release()
return MyLog.log
```
关于python中线程的学习,不懂的同学可以点击这里[https://my.oschina.net/u/3041656/blog/794357](https://my.oschina.net/u/3041656/blog/794357),进行学习哦。好了,至此log的内容也结束了。
下面,我们继续搭建,这次要做的,是configHttp.py的内容。没错,我们开始配置接口文件啦!
下面是接口文件中主要部分的内容,让我们一起来看看吧。
```
# -*- coding: utf-8 -*-
import requests
from interfaceTest import readConfig
# from common.Log import MyLog as Log
from interfaceTest.common.Log import MyLog as Log
localReadConfig = readConfig.ReadConfig()
class ConfigHttp:
def __init__(self):
global host, port, timeout
host = localReadConfig.get_http("baseurl")
port = localReadConfig.get_http("port")
timeout = localReadConfig.get_http("timeout")
self.log = Log.get_log()
self.logger = self.log.get_logger()
self.headers = {}
self.params = {}
self.data = {}
self.url = None
self.files = {}
def set_url(self, url):
self.url = host + url
def set_headers(self, header):
self.headers = header
def set_params(self, param):
self.params = param
def set_data(self, data):
self.data = data
def set_files(self, file):
self.files = file
# defined http get method
def get(self):
try:
response = requests.get(self.url, params=self.params, headers=self.headers, timeout=float(timeout))
# response.raise_for_status()
return response
except TimeoutError:
self.logger.error("Time out!")
return None
# defined http post method
def post(self):
try:
response = requests.post(self.url, headers=self.headers, data=self.data, files=self.files, timeout=float(timeout))
# response.raise_for_status()
return response
except TimeoutError:
self.logger.error("Time out!")
return None
```
这次是用python自带的requests来进行接口测试的,相信有心的朋友已经看出来了,python+requests这个模式是很好用的,它已经帮我们封装好了测试接口的方法,用起来很方便。这里呢,我就拿get和post两个方法来说吧。
* **get方法**
接口测试中见到最多的就是get方法和post方法,其中,get方法用于获取接口的测试,说白了,就是说,使用get的接口,都不会对后台数据进行更改,而且get方法在传递参数后,url的格式是这样的:**http://接口地址?key1=value1&key2=value2**。
```
对于requests提供的get方法,有几个常用的参数:
url:显而易见,就是接口的地址url啦
headers:定制请求头(headers),例如:content-type = application/x-www-form-urlencoded
params:用于传递测试接口所要用的参数,这里我们用python中的字典形式(key:value)进行参数的传递。
timeout:设置接口连接的最大时间(超过该时间会抛出超时错误)
现在,各个参数我们已经知道是什么意思了,剩下的就是往里面填值。
```
举个栗子:
```
url="http://api.shein.com/v2/member/logout"
header={"content-type": application/x-www-form-urlencoded}
param={"user_id": 123456,"email": 123456@163.com}
timeout=0.5
requests.get(url, headers=header, params=param, timeout=timeout)
```
* **post方法**
与get方法类似,只要设置好对应的参数,就可以了。下面就直接举个栗子,直接上代码吧:
```
url="http://api.shein.com/v2/member/login"
header={"content-type": application/x-www-form-urlencoded}
data={"email": 123456@163.com,"password": 123456}
timeout=0.5
requests.post(url, headers=header, data=data, timeout=timeout)
```
这里我们需要说明一下,post方法中的参数,我们不在使用params进行传递,而是改用data进行传递了。下面我们来探(了)讨(解)下接口的返回值。
依然只说常用的返回值的操作。
```
text:获取接口返回值的文本格式
json():获取接口返回值的json()格式
status_code:返回状态码(成功为:200)
headers:返回完整的请求头信息(headers['name']:返回指定的headers内容)
encoding:返回字符编码格式
url:返回接口的完整url地址
```
```
关于失败请求抛出异常,我们可以使用“raise_for_status()”来完成,那么,当我们的请求发生
错误时,就会抛出异常。在这里提醒下各位朋友,如果你的接口,在地址不正确的时候,会
有相应的错误提示(有时也需要进行测试),这时,千万不能使用这个方法来抛出错误,因
为python自己在链接接口时就已经把错误抛出,那么,后面你将无法测试期望的内容。而且
程序会直接在这里当掉,以错误来计。
```
快,我想学(看)习(看)common.py里的内容。
```
# -*- coding: utf-8 -*-
import os
from interfaceTest.common import configHttp
from interfaceTest.readConfig import proDir
from xlrd import open_workbook
from xml.etree import ElementTree
from interfaceTest.common.Log import MyLog as Log
localConfigHttp = configHttp.ConfigHttp()
log = Log.get_log()
logger = log.get_logger()
# 从excel文件中读取测试用例
def get_xls(xls_name, sheet_name):
cls = []
# get xls file's path
xlsPath = os.path.join(proDir, "testFile", xls_name)
# open xls file
file = open_workbook(xlsPath)
# get sheet by name
sheet = file.sheet_by_name(sheet_name)
# get one sheet's rows
nrows = sheet.nrows
for i in range(nrows):
if sheet.row_values(i)[0] != u'case_name':
cls.append(sheet.row_values(i))
return cls
# 从xml文件中读取sql语句
database = {}
def set_xml():
if len(database) == 0:
sql_path = os.path.join(proDir, "testFile", "SQL.xml")
tree = ElementTree.parse(sql_path)
for db in tree.findall("database"):
db_name = db.get("name")
# print(db_name)
table = {}
for tb in db.getchildren():
table_name = tb.get("name")
# print(table_name)
sql = {}
for data in tb.getchildren():
sql_id = data.get("id")
# print(sql_id)
sql[sql_id] = data.text
table[table_name] = sql
database[db_name] = table
def get_xml_dict(database_name, table_name):
set_xml()
database_dict = database.get(database_name).get(table_name)
return database_dict
def get_sql(database_name, table_name, sql_id):
db = get_xml_dict(database_name, table_name)
sql = db.get(sql_id)
return sql
```
上面就是我们common的两大主要内容了
```
我们利用xml.etree.Element来对xml文件进行操作,然后通过我们自定义的方法,根据传递
不同的参数取得不(想)同(要)的值。
利用xlrd来操作excel文件,注意啦,我们是用excel文件来管理测试用例的。
听起来会不会有点儿懵,小编刚学时也很懵,看文件就好理解了。
```
excel文件:
![](https://img.kancloud.cn/2f/75/2f75e8acfb9dd6e1c98211ba9e79a536_1155x220.png)
xml文件:
![](https://img.kancloud.cn/bc/c7/bcc727119e7361e36144e4dc3644a5fb_747x255.png)
还有不会的小伙伴,可以参见这两篇文章学习下:excel:[https://my.oschina.net/u/3041656/blog/819892](https://my.oschina.net/u/3041656/blog/819892) xml:[https://my.oschina.net/u/3041656/blog/819895](https://my.oschina.net/u/3041656/blog/819895)
接下来,我们看看数据库和发送邮件吧(也可根据需要,不写该部分内容)
先看老朋友“数据库”吧。
小编这次使用的是MySQL数据库,所以我们就以它为例吧。
```
# configDB.py
# -*- coding: utf-8 -*-
from distutils import config
import pymysql
from interfaceTest import readConfig
from interfaceTest.common.Log import MyLog as Log
localReadConfig = readConfig.ReadConfig()
class MyDB:
global host, username, password, port, database, readConfig
host = localReadConfig.get_db("host")
username = localReadConfig.get_db("username")
password = localReadConfig.get_db("password")
port = localReadConfig.get_db("port")
database = localReadConfig.get_db("database")
config = {
'host': str(host),
'user': username,
'passwd': password,
'port': int(port),
'db': database
}
def __init__(self):
self.log = Log.get_log()
self.logger = self.log.get_logger()
self.db = None
self.cursor = None
def connectDB(self):
try:
# connect to DB
self.db = pymysql.connect(**config)
# create cursor
self.cursor = self.db.cursor()
print("Connect DB successfully!")
except ConnectionError as ex:
self.logger.error(str(ex))
def executeSQL(self, sql, params):
self.connectDB()
# executing sql
self.cursor.execute(sql, params)
# executing by committing to DB
self.db.commit()
return self.cursor
def get_all(self, cursor):
value = cursor.fetchall()
return value
def get_one(self, cursor):
value = cursor.fetchone()
return value
def closeDB(self):
self.db.close()
print("Database closed!")
```
这就是完整的数据库的文件啦。因为小编的需求对数据库的操作不是很复杂,所以这些已基本满足要求啦。注意下啦,在此之前,请朋友们先把pymysql装起来!安装的方法很简单,由于小编是使用pip来管理python包安装的,所以只要进入python安装路径下的pip文件夹下,执行以下命令即可:
```
pip3 install pymysql
```
```
在整个文件中,我们并没有出现具体的变量值哦,为什么呢?没错,因为前面我们写了config.ini文件,
所有的数据库配置信息都在这个文件内哦,是不是感觉很方便呢,以后就算变更数据库了,也只要修
改config.ini文件的内容就可以了,结合前面测试用例的管理(excel文件),sql语句的存放(xml文件),
还有接下来我们要说的,businessCommon.py和存放具体case的文件夹,那么我们就已经将数据和业
务分开啦,哈哈哈,想想以后修改测试用例内容,sql语句神马的工作,再也不用每个case都修改,只
要改几个固定的文件。
```
回归上面的configDB.py文件,内容很简单,相信大家都能看得懂,就是连接数据库,执行sql,获取结果,最后关闭数据库,没有什么不一样的地方。还有不明白的朋友可以查看这个链接进行学习:[http://www.runoob.com/python/python-mysql.html](http://www.runoob.com/python/python-mysql.html)
<br />
该谈谈邮件啦,你是不是也遇到过这样的问题:每次测试完之后,都需要给开发一份测试报告。那么,对于我这样的懒人,是不愿意老是找人家开发的,所以,我就想,每次测试完,我们可以让程序自己给开发人员发一封email,告诉他们,测试已经结束了,并且把测试报告以附件的形式,通过email发送给开发者的邮箱,这样岂不是爽哉!
所以,configEmail.py应运而生。
```
# -*- coding: utf-8 -*-
import os
import smtplib
from email.mine.multipart import MIMEMultipart
from email.mime.text import MIMEText
from datetime import datetime
import threading
import readconfig
from interfaceTest import readConfig
from interfaceTest.common.Log import MyLog
import zipfile
import glob
localReadConfig = readConfig.ReadConfig()
class Email:
def __init__(self):
global host, user, password, port, sender, title, content
host = localReadConfig.get_email("mail_host")
user = localReadConfig.get_email("mail_user")
password = localReadConfig.get_email("mail_pass")
port = localReadConfig.get_email("mail_port")
sender = localReadConfig.get_email("sender")
title = localReadConfig.get_email("subject")
content = localReadConfig.get_email("content")
self.value = localReadConfig.get_email("receiver")
self.receive = []
# get receive list
for n in str(self.value).split("/"):
self.receive.append(n)
# defined email subject
date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.subject = title + " " + date
self.log = MyLog.get_log()
self.logger = self.log.get_logger()
self.msg = MIMEMultipart('mixed')
def config_header(self):
self.msg['subject'] = self.subject
self.msg['from'] = sender
self.msg['to'] = ";".join(self.receive)
def config_content(self):
content_plain = MIMEText(content, 'plain', 'utf-8')
self.msg.attach(content_plain)
def config_file(self):
# if the file content is not null, then config the email file
if self.check_file():
reportpath = self.log.get_result_path()
zippath = os.path.join(readConfig.proDir, "result", "test.zip")
# zip file
files = glob.glob(reportpath + '\*')
f = zipfile.ZipFile(zippath, 'w', zipfile.ZIP_DEFLATED)
for file in files:
f.write(file)
f.close()
reportfile = open(zippath, 'rb').read()
filehtml = MIMEText(reportfile, 'base64', 'utf-8')
filehtml['Content-Type'] = 'application/octet-stream'
filehtml['Content-Disposition'] = 'attachment; filename="test.zip"'
self.msg.attach(filehtml)
def check_file(self):
reportpath = self.log.get_report_path()
if os.path.isfile(reportpath) and not os.stat(reportpath) == 0:
return True
else:
return False
def send_email(self):
self.config_header()
self.config_content()
self.config_file()
try:
smtp = smtplib.SMTP()
smtp.connect(host)
smtp.login(user, password)
smtp.sendmail(sender, self.receive, self.msg.as_string())
smtp.quit()
self.logger.info("The test report has send to developer by email")
except Exception as ex:
self.logger.error(str(ex))
class MyEmail:
email = None
mutex = threading.Lock()
def __init__(self):
pass
@staticmethod
def get_email():
if MyEmail.email is None:
MyEmail.mutex.acquire()
MyEmail.email = Email()
MyEmail.mutex.release()
return MyEmail.email
if __name__ == "__main__":
email = MyEmail.get_email()
```
关于python对email的操作,上面代码看不太明白的朋友,请移步这里继续学习:[https://my.oschina.net/u/3041656/blog/819937](https://my.oschina.net/u/3041656/blog/819937)
<br />
离成功不远了,简单说明下HTMLTestRunner.py文件。
![](https://img.kancloud.cn/39/2c/392ccbe0553106ccb980bfa4557af03b_1214x581.png)
runAll.py这是我们整个框架运行的入口,上面内容完成后,这是最后一步啦,写完它,我们的框架就算是完成了。
```
# -*- coding: utf-8 -*-
import unittest
import os
from interfaceTest import readConfig
from interfaceTest.common import HTMLTestRunner
from interfaceTest.common.common import logger
def set_case_list(self):
fb = open(self.caseListFile)
for value in fb.readlines():
data = str(value)
if data != '' and not data.startswith("#"):
self.caseListFile.append(data.replace("\n", ""))
fb.close()
def set_case_suite(self):
self.set_case_list()
test_suite = unittest.TestSuite()
suite_model = []
for case in self.caseList:
case_file = os.path.join(readConfig.proDir, "testCase")
print(case_file)
case_name = case.split("/")[-1]
print(case_name + ".py")
discover = unittest.defaultTestLoader.discover(case_file, pattern=case_name + '.py', top_level_dir=None)
suite_model.append(discover)
if len(suite_model) > 0:
for suite in suite_model:
for test_name in suite:
test_suite.addTest(test_name)
else:
return None
return test_suite
def on_off(args):
pass
def run(self):
try:
suit = self.set_case_suite()
if suit is not None:
logger.info("*********TEST START*********")
fp = open(resultPath, 'wb')
runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title='Test Report', description='Test Description')
runner.run(suit)
else:
logger.info("Have no case to test.")
except Exception as ex:
logger.error(str(ex))
finally:
logger.info("*********TEST END**********")
# send test report by email
if int(on_off) == 0:
self.email.send_email()
elif int(on_off) == 1:
logger.info("Doesn't send report email to developer.")
else:
logger.info("Unknow state.")
```
上面我贴出了runAll里面的主要部分,首先我们要从caselist.txt文件中读取需要执行的case名称,然后将他们添加到python自带的unittest测试集中,最后执行run()函数,执行测试集。
<br />
终于呢,整个接口自动化框架已经讲完了。之前的之前贴出的目录结构中的文件还有没说到的。
![](https://img.kancloud.cn/b4/e5/b4e5634a259685902ddae85a53ce9018_259x159.png)
result文件夹会在首次执行case时生成,并且以后的测试结果都会被保存在该文件夹下,同时每次测试的文件夹都是用系统时间命名,里面包含了两个文件,log文件和测试报告。
![](https://img.kancloud.cn/0d/70/0d70181fc111c9abd18cd46a5323b8b4_282x426.png)
testCase文件夹下,存放我们写的具体的测试case啦,上面这些就是小编写的一些。注意喽,所有的case名称都要以test开头来命名哦,这是因为,unittest在进行测试时会自动匹配testCase文件夹下面所有test开头的.py文件。
![](https://img.kancloud.cn/a4/4b/a44b4c2265934a022b5c064bd722a4d5_250x110.png)
testFile文件夹下,放置我们测试时用来管理测试用例的excel文件和用于数据库查询的sql语句的xml文件哦。
最后就是caselist.txt文件了,就让你们瞄一眼吧:
![](https://img.kancloud.cn/27/d4/27d41ec910993fb9b39d88f52cb836aa_319x303.png)
凡是没有被注释掉的,都是要被执行的case名称啦。在这里写上你要执行的case名称就可以啦。
- Linux
- Linux 文件权限概念
- 重点总结
- Linux 文件与目录管理
- 2.1 文件与目录管理
- 2.2 文件内容查阅
- 文件与文件系统的压缩,打包与备份
- 3.1 Linux 系统常见的压缩指令
- 3.2 打包指令: tar
- vi/vim 程序编辑器
- 4.1 vi 的使用
- 4.2 vim编辑器删除一行或者多行内容
- 进程管理
- 5.1 常用命令使用技巧
- 5.2 进程管理
- 系统服务 (daemons)
- 6.1 通过 systemctl 管理服务
- Linux 系统目录结构
- Linux yum命令
- linux系统查看、修改、更新系统时间(自动同步网络时间)
- top linux下的任务管理器
- Linux基本配置
- CentOS7开启防火墙
- CentOS 使用yum安装 pip
- strace 命令
- Linux下设置固定IP地址
- 查看Linux磁盘及内存占用情况
- Mysql
- 关系数据库概述
- 数据库技术
- 数据库基础语句
- 查询语句(--重点--)
- 约束
- 嵌套查询(子查询)
- 表emp
- MySQL数据库练习
- 01.MySQL数据库练习数据
- 02.MySQL数据库练习题目
- 03.MySQL数据库练习-答案
- Mysql远程连接数据库
- Python
- python基础
- Python3中字符串、列表、数组的转换方法
- python字符串
- python安装、pip基本用法、变量、输入输出、流程控制、循环
- 运算符及优先级、数据类型及常用操作、深浅拷贝
- 虚拟环境(virtualenv)
- 网络编程
- TCP/IP简介
- TCP编程
- UDP编程
- 进程和线程
- 访问数据库
- 使用SQLite
- 使用MySQL
- Web开发
- HTML简介
- Python之日志处理(logging模块)
- 函数式编程
- 高阶函数
- python报错解决
- 启动Python时报“ImportError: No module named site”错误
- python实例
- 01- 用python解决数学题
- 02- 冒泡排序
- 03- 邮件发送(smtplib)
- Django
- 01 Web应用
- Django3.2 教程
- Django简介
- Django环境安装
- 第一个Django应用
- Part 1:请求与响应
- Part 2:模型与后台
- Part 3:视图和模板
- Part 4:表单和类视图
- Part 5:测试
- Part 6:静态文件
- Part 7:自定义admin
- 第一章:模型层
- 实战一:基于Django3.2可重用登录与注册系统
- 1. 搭建项目环境
- 2. 设计数据模型
- 3. admin后台
- 4. url路由和视图
- 5. 前端页面设计
- 6. 登录视图
- 7. Django表单
- 8. 图片验证码
- 9. session会话
- 10. 注册视图
- 实战二:Django3.2之CMDB资产管理系统
- 1.项目需求分析
- 2.模型设计
- 3.数据收集客户端
- 4.收集Windows数据
- 5.Linux下收集数据
- 6.新资产待审批区
- 7.审批新资产
- django 快速搭建blog
- imooc-Django全栈项目开发实战
- redis
- 1.1 Redis简介
- 1.2 安装
- 1.3 配置
- 1.4 服务端和客户端命令
- 1.5 Redis命令
- 1.5.1 Redis命令
- 1.5.2 键(Key)
- 1.5.3 字符串(string)
- 1.5.4 哈希(Hash)
- 1.5.5 列表(list)
- 1.5.6 集合(set)
- 1.5.7 有序集合(sorted set)
- Windows
- Win10安装Ubuntu子系统
- win10远程桌面身份验证错误,要求的函数不受支持
- hm软件测试
- 02 linux基本命令
- Linux终端命令格式
- Linux基本命令(一)
- Linux基本命令(二)
- 02 数据库
- 数据库简介
- 基本概念
- Navicat使用
- SQL语言
- 高级
- 03 深入了解软件测试
- day01
- 04 python基础
- 语言基础
- 程序中的变量
- 程序的输出
- 程序中的运算符
- 数据类型基础
- 数据序列
- 数据类型分类
- 字符串
- 列表
- 元组
- 字典
- 列表与元组的区别详解
- 函数
- 案例综合应用
- 列表推导式
- 名片管理系统
- 文件操作
- 面向对象基础(一)
- 面向对象基础(二)
- 异常、模块
- 05 web自动化测试
- Day01
- Day02
- Day03
- Day04
- Day05
- Day06
- Day07
- Day08
- 06 接口自动化测试
- 软件测试面试大全2020
- 第一章 测试理论
- 软件测试面试
- 一、软件基础知识
- 二、网络基础知识
- 三、数据库
- SQL学生表 — 1
- SQL学生表 — 2
- SQL查询 — 3
- SQL经典面试题 — 4
- 四、linux
- a. linux常用命令
- 五、自动化测试
- 自动化测试
- python 笔试题
- selenium面试题
- 如何判断一个页面上元素是否存在?
- 如何提高脚本的稳定性?
- 如何定位动态元素?
- 如何通过子元素定位父元素?
- 如果截取某一个元素的图片,不要截取全部图片
- 平常遇到过哪些问题?如何解决的
- 一个元素明明定位到了,点击无效(也没报错),如果解决?
- selenium中隐藏元素如何定位?(hidden、display: none)
- 六、接口测试
- 接口测试常规面试题
- 接口自动化面试题
- json和字典dict的区别?
- 测试的数据你放在哪?
- 什么是数据驱动,如何参数化?
- 下个接口请求参数依赖上个接口的返回数据
- 依赖于登录的接口如何处理?
- 依赖第三方的接口如何处理
- 不可逆的操作,如何处理,比如删除一个订单这种接口如何测试
- 接口产生的垃圾数据如何清理
- 一个订单的几种状态如何全部测到,如:未处理,处理中,处理失败,处理成功
- python如何连接数据库操作?
- 七、App测试
- 什么是activity?
- Activity生命周期?
- Android四大组件
- app测试和web测试有什么区别?
- android和ios测试区别?
- app出现ANR,是什么原因导致的?
- App出现crash原因有哪些?
- app对于不稳定偶然出现anr和crash时候你是怎么处理的?
- app的日志如何抓取?
- logcat查看日志步骤
- 你平常会看日志吗, 一般会出现哪些异常
- 抓包工具
- fiddler
- Wireshark
- 安全/渗透测试
- 安全性测试都包含哪些内容?
- 开放性思维题
- 面试题
- 字节测试面试
- 一、计算机网络
- 二、操作系统
- 三、数据库
- 四、数据结构与算法
- 五、Python
- 六、Linux
- 七、测试用例
- 八、智力/场景题
- 九、开放性问题
- python3_收集100+练习题(面试题)
- python3_100道题目答案
- 接口测试
- 接口测试实例_01
- python+requests接口自动化测试框架实例详解
- 性能测试
- 性能测试流程
- 性能测试面试题
- 如何编写性能测试场景用例
- 性能测试:TPS和QPS的区别
- jmeter
- jmeter安装配置教程
- Jmeter性能测试 入门
- PyCharm
- 快捷工具
- 1-MeterSphere
- 一、安装和升级
- 2- MobaXterm 教程
- 3-fiddler抓包
- 4-Xshell
- Xshell的安装和使用
- Xshell远程连接失败怎么解决
- 5-Vmware
- Vmware提示以独占方式锁定此配置文件失败
- Windows10彻底卸载VMWare虚拟机步骤
- VM ware无法关机,虚拟机繁忙
- VMware虚拟机下载与安装
- 解决VM 与 Device/Credential Guard 不兼容。在禁用 Device/Credential Guard 后,可以运行 VM 的方法
- VMware虚拟机镜像克隆与导入
- 6-WPS
- 1.WPS文档里的批注怎么删除
- 2.wps表格中设置图表的坐标
- 3. wps快速绘制数学交集图
- 7-MongoDB
- Win10安装配置MongoDB
- Navicat 15.x for MongoDB安装破解教程
- Apache
- apache层的账户权限控制,以及apache黑名单白名单过滤功能
- HTTP / HTTPS协议
- HTTP协议详解
- 代理
- 状态码详解
- HTTPS详解
- Selenium3+python3
- (A) selenium
- selenium自动化环境搭建(Windows10)
- 火狐firebug和firepath插件安装方法(最新)
- 元素定位工具和方法
- Selenium3+python3自动化
- 新手学习selenium路线图---学前篇
- 1-操作浏览器基本方法
- 2-八种元素定位方法
- 3-CSS定位语法
- 4-登录案例
- 5-定位一组元素find_elements
- 6-操作元素(键盘和鼠标事件)
- 7-多窗口、句柄(handle)
- 8-iframe
- 9-select下拉框
- 10-alert\confirm\prompt
- 11-JS处理滚动条
- 12-单选框和复选框(radiobox、checkbox)
- 13-js处理日历控件(修改readonly属性)
- 14-js处理内嵌div滚动条
- 15-table定位
- 16-js处理多窗口
- 17-文件上传(send_keys)
- 18-获取百度输入联想词
- 19-处理浏览器弹窗
- 20-获取元素属性
- 21-判断元素存在
- 22-爬页面源码(page_source)
- 23-显式等待(WebDriverWait)
- 24-关于面试的题
- 25-cookie相关操作
- 26-判断元素(expected_conditions)
- 27-判断title(title_is)
- 28-元素定位参数化(find_element)
- 29-18种定位方法(find_elements)
- 30- js解决click失效问题
- 31- 判断弹出框存在(alert_is_present)
- 32- 登录方法(参数化)
- 33- 判断文本(text_to_be_present_in_element)
- 34- unittest简介
- 35- unittest执行顺序
- 36- unittest之装饰器(@classmethod)
- 37- unittest之断言(assert)
- 38- 捕获异常(NoSuchElementException)
- 39- 读取Excel数据(xlrd)
- 40- 数据驱动(ddt)
- 41- 异常后截图(screenshot)
- 42- jenkins持续集成环境搭建
- 43- Pycharm上python和unittest两种运行方式
- 44- 定位的坑:class属性有空格
- 45- 只截某个元素的图
- 46- unittest多线程执行用例
- 47- unittest多线程生成报告(BeautifulReport)
- 48- 多线程启动多个不同浏览器
- (B) python3+selenium3实现web UI功能自动化测试框架
- (C) selenium3常见报错处理
- 书籍
- (D)Selenium3自动化测试实战--基于Python语
- 第4章 WebDriver API
- 4.1 从定位元素开始
- 4.2 控制浏览器
- 4.3 WebDriver 中的常用方法
- 4.4 鼠标操作
- 4.5 键盘操作
- 4.6 获得验证信息
- 4.7 设置元素等待
- 4.8 定位一组元素
- 4.9 多表单切换
- 4.10 多窗口切换
- 4.11 警告框处理
- 4.12 下拉框处理
- 4.13 上传文件
- 4.14 下载文件
- 4.15 操作cookie
- 4.16 调用JavaScript
- 4.17 处理HTML5视频播放
- 4.18 滑动解锁
- 4.19 窗口截图
- 第5章 自动化测试模型
- 5.3 模块化与参数化
- 5.4 读取数据文件
- 第6章 unittest单元测试框架
- 6.1 认识unittest