每日打卡-01-字符串填空
起止时间: 2020/10/19 09:00 ~ 2020/11/21 23:59
知识点参考 https://www.cnblogs.com/superhin/p/13837611.html
- 编写一个函数,输入一个text参数,将函数参数放入字符串 "//*[text()='所传text参数']"指定位置中,并打印这个字符串
- 定义一个多行文本
tpl = '''
<html>
<head><meta charset="UTF-8"><title>title变量位置</title></head>
<body>
<h1>report_name变量位置</h1>
<div>开始时间:start_at变量位置 运行时间:duration变量位置 秒</div>
<div>总数:testRuns变量位置 通过:successes变量位置 失败:failures变量位置 异常: errors变量位置</div>
</body>
<html>'''
已知变量
title="测试报告"
report_name="接口测试报告"
start_at="2020-09-10 10:00:00"
duration=3
testRuns=20
successes=15
failures=4
errors=1
把上述变量渲染到tpl模板变量中,并将得到的包含数据的字符串按保存文本文件的方式,以utf-8编码的格式保存为一个名称为report.html的文件。
参考答案
# 1
def xpath(text):
return f'//*[text()="{text}"]
# 2
tpl = '''
<html>
<head><meta charset="UTF-8"><title>{title}</title></head>
<body>
<h1>{report_name}</h1>
<div>开始时间:{start_at} 运行时间:{duration} 秒</div>
<div>总数:{testRuns} 通过:{successes} 失败:{failures} 异常: {errors}</div>
</body>
<html>'''
title="测试报告"
report_name="接口测试报告"
start_at="2020-09-10 10:00:00"
duration=3
testRuns=20
successes=15
failures=4
errors=1
html = tpl.format(title=title, report_name=report_name, start_at=start_at, duration=duration,
testRuns=testRuns, successes=successes, failures=failures, errors=errors)
with open('report.html, 'w', encoding='utf-8') as f:
f.write(html)
每日打卡-02-列表和字典解包
起止时间: 2020/10/20 09:00 ~ 2020/11/21 23:59
知识点:https://www.cnblogs.com/superhin/p/13837849.html
- 有一个变量
search_ipt_loc = ('id', 'kw')
和函数
def find_element(by, value):
print(f'通过{by}={value}定位元素')
怎样调用find_element函数将search_ipt_loc中的数据传入?
- 有一个字典
db_conf = {'host': '127.0.0.1', 'port': 3306, 'user': 'test', 'password': '11111', 'db': 'abc'}
和函数
def connect(host,port,user,password,db):
print(f'连接数据{host}成功')
怎么调用函数把db_conf中的数据传入?
参考答案
search_ipt_loc = ('id', 'kw')
def find_element(by, value):
print(f'通过{by}={value}定位元素')
db_conf = {'host': '127.0.0.1', 'port': 3306, 'user': 'test', 'password': '11111', 'db': 'abc'}
def connect(host,port,user,password,db):
print(f'连接数据{host}成功')
find_element(*search_ipt_loc)
connect(**db_conf)
每日打卡-03-使用对象在方法间共享属性
起止时间: 2020/10/21 09:00 ~ 2020/11/21 23:59
知识点:https://www.cnblogs.com/superhin/p/13841854.html
- 设计一个名为Page的类,需要传入一个名为driver的参数。其中包含以下3个方法。
(1)统一定位方法
def find_element(by, value):
...
by参数的选择是"id","name","class name", "tag name", "link text", "partial link text", "xpath", "css selector"
value是具体对应的元素属性或定位表达式字符串
当by=id时使用find_element_by_id方法定位,依次类推
并返回定位到的元素
(2)元素点击方法
def click(by, value):
...
使用by,value调用上面封装的find_element方法得到元素,然后进行点击
(3)元素输入方法
def type(by, value, text):
...
使用by,value调用上面封装的find_element方法得到元素, 然后输入text的值
完成后,在类下使用以下脚本进行测试
from selenium import webdriver
from time import sleep
driver = webdriver.Chrome()
driver.get('https://www.baidu.com')
baidu = Page(driver)
baidu.type('id', 'kw', '简书 韩志超')
baidu.click('id', 'su')
sleep(3)
driver.quit()
参考答案
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.remote.webelement import WebElement
class Page(object):
def __init__(self, driver: WebDriver):
self.driver = driver
def find_element(self, by: str, value: str) -> WebElement:
method_map = {
'id': self.driver.find_element_by_id,
'name': self.driver.find_element_by_name,
'class name': self.driver.find_element_by_class_name,
'tag name': self.driver.find_element_by_tag_name,
'link text': self.driver.find_element_by_link_text,
'partial link text': self.driver.find_element_by_partial_link_text,
'xpath': self.driver.find_element_by_xpath,
'css selector': self.driver.find_element_by_css_selector,
}
if by not in method_map:
raise ValueError('不支持该定位方法')
func = method_map.get(by)
element = func(value)
return element
def click(self, by: str, value: str) -> None:
print(f'点击元素{by}={value}')
self.find_element(by, value).click()
def type(self, by: str, value: str, text: str) -> None:
print(f'元素{by}={value}中输入{text}')
send_key = self.find_element(by, value)
send_key.send_keys(text)
if __name__ == '__main__':
from selenium import webdriver
from time import sleep
driver = webdriver.Chrome()
driver.get("https://www.baidu.com")
baidu = Page(driver)
baidu.type("id", "kw", "简书 韩志超")
baidu.click("id", "su")
sleep(3)
driver.quit()
每日打卡04-使用CSV数据
起止时间: 2020/10/22 09:00 ~ 2020/11/21 23:59
知识点:https://www.cnblogs.com/superhin/p/11495956.html
- 假设我们爬取到一批数据
movies = [
('肖申克的救赎','Tim Robbins', 'https://xiaoshenke.html'),
('霸王别姬','张国荣', 'https://bawangbieji.html'),
('阿甘正传','Tom Hanks', 'https://aganzhengzhuan.html'),
]
将其保存为一个movie.csv文件,并加上标题行movie,player,url
2. 假设我们有一个data.csv文件,内容如下
a,b,excepted
1,2,3
0,0,0
-1,-1,-1
6,0.3,6.3
另外有一个函数
def add(a,b):
return a+b
编写一个测试函数,读取csv中的每行数据,将add(a,b)的返回结果与每行的excepted对比,如果成打印成功
失败则打印失败。
参考答案
import csv
# 1
movies = [
('肖申克的救赎','Tim Robbins', 'https://xiaoshenke.html'),
('霸王别姬','张国荣', 'https://bawangbieji.html'),
('阿甘正传','Tom Hanks', 'https://aganzhengzhuan.html'),
]
header = ('movie','player','url')
with open('movie.csv', 'w', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(header)
writer.writerows(movies)
# 2
def add(a, b):
return a + b
with open('data.csv', encoding='utf-8') as f:
data = csv.reader(f)
for a, b, excepted in data:
a, b, excepted = float(a), float(b), float(excepted)
if add(a, b) == excepted:
print(f'{a}+{b}={excepted} 通过')
else:
print(f'{a}+{b}={excepted} 不通过')
每日打卡-05-两数之和问题
起止时间: 2020/10/23 09:00 ~ 2020/11/21 23:59
知识点:时间复杂度指需要Operate操作的次数的量级
完全遍历一个长度为n的列表则时间复杂度为O(n)。
如果双重循环
for i in range(n):
for j in range(n):
....
则时间复杂度是O(n^2)
- 假设我们有一个列表
l=[1,2,3,4,5,6,7,8] 数据不重复,目标值为6,要求找出数组中两个元素之和等于目标 的数组下标。
要求时间复杂度小于O(n^2)
参考答案
注意,不要使用双重循环,暴力加和来和target对比,正确的做法是单层循环,然后查找target与当前值的差,是否存在于列表中。
但是由于列表的in查询时间复杂度是O(n),即隐含了一层循环,这样效率其实和双重循环是一样的,都是O(n^2)。
这里就可以使用哈希来优化查询差值是否在列表中操作,将O(n)降为O(1),因此总体的效率就会变成O(n^2)->O(n)。
l = [1,2,3,4,5,6,7,8]
set1 = set(list1) # 使用集合已方便查找
target = 6
result = []
for a in list1:
b = target - a
if a < b < target and b in set1: # 在集合中查找,为避免重复,判断a为较小的那个值
result.append((list1.index(a), list1.index(b))) # 列表index取下标的操作为O(1)
print(result)
每日打卡-06-列表字典推导式
起止时间: 2020/10/26 11:08 ~ 2020/10/31 23:59
知识点:https://www.cnblogs.com/superhin/p/13877327.html
- 数据筛选
假设一个活动列表接口数据如下
res = {
'code': 0,
'msg': '成功',
'datas': [
{'activityName': '双11折扣', 'activityId': 12543, 'start_time': '20201020', 'end_time': '20201120', 'state': 1},
{'activityName': '今日折扣', 'activityId': 23413, 'start_time': '20201020', 'end_time': '20201021', 'state': 2},
{'activityName': '大减价', 'activityId': 13265, 'start_time': '20201019', 'end_time': '20201120', 'state': 0},
{'activityName': '每日促销', 'activityId': 19876, 'start_time': '20201020', 'end_time': '20201121', 'state': 0},
{'activityName': '新用户优惠', 'activityId': 15801, 'start_time': '20201020', 'end_time': '20201220', 'state': 1},
]
}
已知state=0为未开始,1为进行中,2为以结束,使用列表推导式得到当前所有进行中的activityId列表,要求每个activityId转为字符串形式。
- Cookies格式转化
假设我们需要通过登录接口绕过Selenium登录操作,我们需要使用requests发送登录接口,得到响应中的cookies然后再使用driver.add_cookie
方法添加的浏览器中。
接口返回的cookies是一个字典格式,如{'Token', 'abcdefg', 'SessionID': '1234567'}
而selenium添加cookie时需要这样的格式,{'name': 'Token': 'value': 'abcdefg'}
和{'name': 'SessionID': 'value': '123456'}
使用推导式实现完成转换
cookies = {'Token':'abcdefg', 'SessionID': '1234567'}
# 转为
cookies = [{'name': 'Token': 'value': 'abcdefg'},{'name': 'SessionID': 'value': '123456'}]
参考答案
# 1
res = {
'code': 0,
'msg': '成功',
'datas': [
{'activityName': '双11折扣', 'activityId': 12543, 'start_time': '20201020', 'end_time': '20201120', 'state': 1},
{'activityName': '今日折扣', 'activityId': 23413, 'start_time': '20201020', 'end_time': '20201021', 'state': 2},
{'activityName': '大减价', 'activityId': 13265, 'start_time': '20201019', 'end_time': '20201120', 'state': 0},
{'activityName': '每日促销', 'activityId': 19876, 'start_time': '20201020', 'end_time': '20201121', 'state': 0},
{'activityName': '新用户优惠', 'activityId': 15801, 'start_time': '20201020', 'end_time': '20201220', 'state': 1},
]
}
activity_ids = [str(item.get('activityId') for item in res.get('datas', [])]
# 2
cookies = {'Token':'abcdefg', 'SessionID': '1234567'}
cookies = [{'name': key, 'value': value} for key, value in cookies.items()]
每日打卡-07-路径组装和获取环境变量
起止时间: 2020/10/27 08:56 ~ 2020/11/27 23:59
知识点:https://www.cnblogs.com/superhin/p/13880748.html
- 有一个项目结构如下
autotest/
data/
data.csv
testcases/
baidu.py
在baidu.py中如何组装出项目的根路径以及data.csv文件的路径
- 在系统中(Win 环境变量设置, Mac
sudo vim ~/.bash_profile
并source ~/.bash_profile
)手工添加两个环境变量
USER=admin
PASSWORD=123456
并在baidu.py中读取出来这两个变量的值
参考答案
import os
# 1
base_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)
data_file = os.path.join(base_dir, 'data', 'data.csv')
# 2
user = os.getenv('USER')
password = os.getenv('PASSWORD')
每日打卡-08-Python类中的不同方法
起止时间: 2020/10/28 12:23 ~ 2020/11/27 23:59
知识点:https://www.cnblogs.com/superhin/p/13884123.html
- Python类中有哪几种方法,分别怎么调用?
参考答案
3种,类方法,实例方法,静态方法,类方法使用类名调用,
实例方法需要创建实例使用实例调用,静态方法使用类名或者实例都可以调用。
每日打卡-09-使用ini配置文件
起止时间: 2020/10/29 12:23 ~ 2020/11/27 23:59
知识点:https://www.cnblogs.com/superhin/p/13883802.html
- 手工新建一个配置文件config.ini,内容如下
[user]
name=admin
password=123456
is_admin=true
[mysql]
host=10.10.10.10
port=3306
db=apitest
user=root
password=123456
读取mysql段的配置,并得到一个字典变量
db_conf={'host': '10.10.10.10', 'port': 3306, 'db': 'apitest', 'user': 'root', 'password': '123456'}
注意:port需要是整型
参考答案
from configparser import ConfigParser
conf = ConfigParser()
conf.read('/Users/superhin/项目/示例/data.ini')
db_conf = dict(conf['mysql'])
db_conf['port'] = int(db_conf['port'])
print(db_conf)
每日打卡-10-二分查找
起止时间: 2020/10/30 22:08 ~ 2020/11/27 23:59
知识点:二分查找是一种比较快的查找算法,首先需要序列有序。
思想是先用序列中间数和目标值对比,如果目标值小,则从前半部分(小于中间数)重复此查找,否则从后半部分重复此查找,直到目标值左右两边没有值。
-
- 已知一个列表
l = [0, 9, 8, 1, 10, 2, 5, 3, 11, 6, 13, 100, 24]
l.sort() # 对列表进行排序
此时l=[0, 1, 2, 3, 5, 6, 8, 9, 10, 11, 13, 24, 100]
编写一个二分查找算法,查找排序后l中8的索引。
参考答案
l = [0, 1, 2, 3, 5, 6, 8, 9, 10, 11, 13, 24, 100]
def bin_find(l, target):
low, high = 0, len(l) - 1 # low, high是两个游标, 通过不断地逼近来找到该数字
while low < high:
mid = (high + low) // 2 # 取中间数索引, 注意是 low + high
if target > l[mid]: # 如果目标值比中间数大,移动下游标
print(target, '>', l[mid])
low = mid
elif target < l[mid]: # 如果目标比中间数小,移动上游标
print(target, '<', l[mid])
high = mid
else:
print('找到了, 索引', mid)
return mid
print('未找到')
bin_find(l, 8)
每日打卡-11-高效的集合操作
起止时间: 2020/11/02 09:48 ~ 2020/12/02 23:59
知识点:https://www.cnblogs.com/superhin/p/13913335.html
- 100w条数据,使用列表和集合哪个查询效率比较高?为什么?
- 为什么Redis查询很快?
- 在A-B测试中,假设A桶返回的数据为
{'code': 0, 'msg': 'success', 'data': [{'productId': 123, 'productName': 'lv包'},{'productId': 321, 'productName': 'prada包'},{'productId': 542, 'productName': 'gucci包'},{'productId': 412, 'productName': 'fendi包'},{'productId': 631, 'productName': 'ysl包'},{'productId': 221, 'productName': 'mcm包'},{'productId': 541, 'productName': 'penko包'}]}
B桶数据为
{'code': 0, 'msg': 'success', 'data': [{'productId': 123, 'productName': 'lv包'},{'productId': 321, 'productName': 'proda包'},{'productId': 541, 'productName': 'gucci包'},{'productId': 412, 'productName': 'fendi包'},{'productId': 631, 'productName': 'ysl包'},{'productId': 121, 'productName': 'mcm包'},{'productId': 541, 'productName': 'penko包'}]}
利用集合操作快速找出A桶和B桶的数据差异。
参考答案
- 集合查询效率高,因为集合是基于哈希算法的,无论数据量多少,查询只需要一次操作。
- Redis是存储与内存中的基于哈希算法的key-value键值对,无论数据量多少,单次查询只需要一次操作。
- 代码如下
res_a = {'code': 0, 'msg': 'success', 'data': [{'productId': 123, 'productName': 'lv包'}, {'productId': 321, 'productName': 'prada包'}, {'productId': 542, 'productName': 'gucci包'}, {
'productId': 412, 'productName': 'fendi包'}, {'productId': 631, 'productName': 'ysl包'}, {'productId': 221, 'productName': 'mcm包'}, {'productId': 541, 'productName': 'penko包'}]}
res_b = {'code': 0, 'msg': 'success', 'data': [{'productId': 123, 'productName': 'lv包'}, {'productId': 321, 'productName': 'proda包'}, {'productId': 541, 'productName': 'gucci包'}, {
'productId': 412, 'productName': 'fendi包'}, {'productId': 631, 'productName': 'ysl包'}, {'productId': 121, 'productName': 'mcm包'}, {'productId': 541, 'productName': 'penko包'}]}
# 将 res_a['data']中, 每一项字典,如{'productId': 123, 'productName': 'lv包'},改为可哈希的元祖(123, 'lv包')格式
data_a = [(item['productId'], item['productName']) for item in res_a['data']]
data_b = [(item['productId'], item['productName']) for item in res_b['data']]
data_a = set(data_a) # 转为集合
data_b = set(data_b)
print('A桶有-B桶无', data_a - data_b)
print('B桶有-A桶无', data_b - data_a)
打印结果
A桶有-B桶无 {(321, 'prada包'), (221, 'mcm包'), (542, 'gucci包')}
B桶有-A桶无 {(541, 'gucci包'), (121, 'mcm包'), (321, 'proda包')}
每日打开-12-使用Yaml文件
起止时间: 2020/11/03 09:18 ~ 2020/12/03 23:59
JSON是一种非常流行的数据格式,支持多种数据类型并支持嵌套,并可以快速转为Python中的字典或列表类型。
然而由于JSON格式严格、不支持注释。因此有了更简洁强大的YAML格式,YAML兼容JSON格式,表述起来非常简洁易用,因此成为很多开发者欢迎。
知识点:<https://www.cnblogs.com/superhin/p/11503756.html?
安装方式: pip install pyyaml
假设我们想将一个用例描述为如下,一个用例,有用例名称,并包含多个步骤,每个步骤都包含command操作、target目标、value值三个属性,command支持open/click/type三种操作,taget和value允许为空,其中
target支持'id=,' 'name=', 'class name'='', 'css selector=', ....等八种。
已百度搜索为例,我们可以用字典描述为
{
'case_name': '百度搜索',
'steps': [
{'command': 'open', 'target': 'https://www.baidu.com/', 'value': ''},
{'command': 'type', 'target': 'id=kw', 'value': '双11'},
{'command': 'click', 'target': 'id=su', 'value': ''}
]
}
- 按yaml格式,手动将数据保存为一个yaml文件,并用Python读取出来。
- 解析并使用Selenium执行该条用例。
参考答案
- 格式如下
# 文件名: yaml_case.yaml
case_name: 百度搜索
steps:
- command: open
target: https://www.baidu.com/
value:
- command: type
target: id=kw
value: 双11
- command: click
target: id=su
value:
- 代码如下
import yaml
from selenium import webdriver
# 定义三个操作函数, 并保持参数签名统一
def do_open(driver, target, value=None):
print('打开页面', target)
driver.get(target)
def do_type(driver, target, value):
print(f'在 {target} 输入 {value}')
elm_loc = target.split('=') # 分割得到定位方式和定位器
driver.find_element(*elm_loc).send_keys(value)
def do_click(driver, target, value=None):
print(f'点击 {target}')
elm_loc = target.split('=')
driver.find_element(*elm_loc).click()
# 使用字典做动作映射
command_map = {
'open': do_open, # 上面定义的do_open函数
'type': do_type, # 上面定义的do_type函数
'click': do_click, # 上面定义的do_click函数
}
def run_yaml(driver, yaml_file):
# 加载yaml数据,并转为字典格式
with open(yaml_file, encoding='utf-8') as f:
data = yaml.safe_load(f)
print('执行用例', data.get('case_name'))
for step in data.get('steps', []):
# 读取每个步骤的 comnand, target, value字段的值
command, target, value = step.get('command'), step.get('target'), step.get('value')
# 获取对应的操作函数
func = command_map.get(command)
# 执行函数
func(driver, target, value)
# 测试一下
if __name__ == "__main__":
driver = webdriver.Chrome()
yaml_file = '/Users/superhin/项目/示例/yaml_case.yaml'
run_yaml(driver, yaml_file)
每日打卡-13-使用日志
起止时间: 2020/11/04 11:32 ~ 2020/12/04 23:59
知识点:https://www.cnblogs.com/superhin/p/13924876.html
- 使用Selenium打开https://www.zhipin.com/,搜索框输入 "测试工程师",并按回车,城市点击 "北京",
定位出首页所有的职位链接。
要求每一步操作前输出INFO级的日志,同时输出到屏幕和文件run.log中,最后运行后,日志输出如下。
2020/11/04 11:31:49 [INFO] 启动浏览器
2020/11/04 11:31:51 [INFO] 打开BOSS直聘
2020/11/04 11:31:53 [INFO] 搜索框输入测试工程师并回车
2020/11/04 11:31:54 [INFO] 点击北京
2020/11/04 11:31:54 [INFO] 定位所有标题连接
2020/11/04 11:31:54 [INFO] 标题个数: 30
2020/11/04 11:31:54 [INFO] 退出浏览器
注意日志格式和不要输出Selenium自带的DEBUG日志。
参考答案
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import logging
cli_handler = logging.StreamHandler() # 输出到屏幕的日志处理器
file_handler = logging.FileHandler(filename='run.log', mode='a', encoding='utf-8') # 输出到文件的日志处理器
logging.basicConfig(level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
datefmt='%Y/%m/%d %H:%M:%S',
handlers=[cli_handler, file_handler] # 添加两个日志处理器
)
logging.info('启动浏览器')
dr = webdriver.Chrome()
dr.implicitly_wait(10)
logging.info('打开BOSS直聘')
dr.get('https://www.zhipin.com/')
logging.info('搜索框输入测试工程师并回车')
dr.find_element('name', 'query').send_keys('测试工程师'+Keys.ENTER)
logging.info('点击北京')
dr.find_element('link text', '北京').click()
logging.info('定位所有标题连接')
links = dr.find_elements('css selector', 'span.job-name>a')
logging.info(f'标题个数: {len(links)}')
logging.info('退出浏览器')
dr.quit()
每日打卡-14-使用Cookies绕过登录
起止时间: 2020/11/05 09:06 ~ 2020/12/04 23:59
知识点:https://www.cnblogs.com/superhin/p/11481803.html
- 使用接口请求获取cookies,直接绕过商之翼系统的登录,直接访问,添加分类链接
http://39.104.14.232/newecshop/admin/category.php?act=add,然后添加一个名为'某某某的分类'的商品分类
参考答案
import requests
from selenium import webdriver
from time import sleep
base_url = 'http://****'
username = '****'
password = '****'
login_url = base_url + '/newecshop/admin/privilege.php' # 登录接口地址
admin_url = base_url + '/newecshop/admin' # 后台登录页地址
add_category_url = base_url + '/newecshop/admin/category.php?act=add' # 添加分类页面地址
# 获取cookie
data = dict(username=username,password=password, act='signin')
res = requests.post(login_url, data=data, allow_redirects=False)
esscp_id = res.cookies.get('ECSCP_ID')
cookie = dict(name='ECSCP_ID',value=esscp_id)
print('cookie' ,cookie)
# 通过添加cookie绕过登录
driver = webdriver.Chrome()
driver.get(admin_url)
driver.add_cookie(cookie)
driver.get(add_category_url) # 需要打开两次
driver.find_element('name', 'cat_name').send_keys('hzc_分类1') # 输入分类名称
driver.find_element('css selector', 'input[type=submit]').click() # 点击确定
sleep(3)
driver.quit()
每日打卡-15-使用Docker及Zalenium
起止时间: 2020/11/06 09:06 ~ 2020/12/04 23:59
知识点:https://www.cnblogs.com/superhin/p/13857584.html
- 安装Docker并安装Zalenium,使用Zalenium提供的Selenium Grid服务运行一条商之翼登录的用例,并查看录像。
请提交代码及截图。
实践题目-无参考答案
每日打卡16-理解Python中的装饰器
起止时间: 2020/11/09 11:37 ~ 2020/12/09 23:59
知识点:https://www.cnblogs.com/superhin/p/13947725.html
以下三个练习完成一项即可提交。
- 编写一个基础装饰器info(func),在运行函数前,打印一条信息
print('调用->{func.name}') # func是函数对象func.__name__可以获取函数名称
并装饰以下函数。
@info
def add(a, b):
return a + b
add(1,2)
- 编写一个可以打印函数参数的装饰器info(func),在函数运行前,打印一条信息
print('调用->add 参数: {args} {kwargs}')
- 编辑一个带参数的装饰器custom_info(verbosity=1):
verbosity默认为1时,在函数运行前打印
print('调用->{func.__name__}')
当verbosity为2时,在函数运行前打印
print('调用->{func.__name__} 参数: {args} {kwargs}') # args kwargs 代表被装饰的函数参数
当verbosity>2时,在函数运行前打印
print('调用->{func.__name__} 参数: {args} {kwargs}')
在函数运行后打印
print('调用->{func.__name__} 结果: {result}') # result代表函数结果
并装饰函数
@custom_info(3)
def add(a, b):
return a + b
add(1,2)
查看结果。
参考答案
- 代码如下
def info(func):
print(f'调用->{func.__name__}')
return func
- 代码如下
def info(func):
def new_func(*args, **kwargs):
print(f'调用->{func.__name__} 参数: {args} {kwargs}')
return func(*args, **kwargs)
return new_func
- 代码如下
def custom_info(verbosity=1):
def info(func):
def new_func(*args, **kwargs):
if verbosity == 1:
print(f'调用->{func.__name__}')
elif verbosity > 1:
print(f'调用->{func.__name__} 参数: {args} {kwargs}')
result = func(*args, **kwargs)
if verbosity > 2:
print(f'调用->{func.__name__} 结果: {result}')
return result
return new_func
return info
每日打卡17-练习日期控件操作
起止时间: 2020/11/10 09:17 ~ 2020/12/09 23:59
知识点:https://www.cnblogs.com/superhin/p/13950534.html
- 下载群文件中的,time.zip
链接: https://pan.baidu.com/s/1ZA8JsV86S-i4uPD9PRgw8w 密码: lee4
并解压,使用浏览器打开其中的date.html,并复制出地址。
使用Selenium输入活动的开始时间和结束时间。
参考答案
from selenium import webdriver
from time import sleep
url = 'file:///Users/superhin/Downloads/Time/date.html'
dr = webdriver.Chrome()
dr.get(url)
js = '''
document.querySelector("input[name='act_start_time']").removeAttribute("readonly");
document.querySelector("input[name='act_stop_time']").removeAttribute("readonly");
'''
dr.execute_script(js)
sleep(.5)
dr.find_element('name', 'act_start_time').send_keys('2020-11-16 00:00')
dr.find_element('name', 'act_stop_time').send_keys('2020-11-27 00:00')
dr.find_element('xpath', '//body').click() # 空白处点击,以关闭日期弹框
sleep(5)
dr.quit()
每日打卡18-使用JSON文件
起止时间: 2020/11/11 09:17 ~ 2020/12/09 23:59
知识点:https://www.cnblogs.com/superhin/p/11502830.html
- 假设我们用例中需要用到登录用户及商品的数据,规划如下
user: # 登录用户数据
username: admin
password: 66666
goods: # 商品相关数据
new_goods_001: # 新商品数据001
goods_name: hzc_dell电脑
cat_name: 电脑
shop_price: 3999
转为字典即
{'goods': {'new_goods_001': {'cat_name': '电脑',
'goods_name': 'hzc_dell电脑',
'shop_price': 3999}},
'user': {'password': '66666', 'username': 'admin'}}
手动将其保存为一个名为data.json的文件,编写脚本,读取其中的数据并还原成字典格式,然后提取并打印出其中的用户名和密码。
参考答案
data.json内容
{
"goods": {
"new_goods_001": {
"cat_name": "电脑",
"goods_name": "hzc_dell电脑",
"shop_price": 3999
}
},
"user": {
"password": "66666",
"username": "admin"
}
}
读取脚本
import json
with open('data.json', encoding='utf-8') as f:
data = json.load(f)
print('用户名', data['user']['username'])
print('密码', data['user']['password'])
每日打卡19-使用Python发送邮件
起止时间: 2020/11/12 09:35 ~ 2020/12/09 23:59
知识点:https://www.cnblogs.com/superhin/p/13950653.html
- 登录自己的qq邮箱并开通smtp服务,使用Python脚本发送一封邮件到superhin@126.com,邮件主题中写上自己的名字。
参考答案
import smtplib # 用于建立smtp连接
from email.mime.text import MIMEText
smtp_server = 'smtp.qq.com'
smtp_user = '***@qq.com'
smtp_password = '****'
def send_email(subject, body, receiver):
msg = MIMEText(body, 'plain', 'utf-8')
msg['From'], msg['To'], msg['Subject'] = smtp_user, receiver, subject
smtp = smtplib.SMTP_SSL(smtp_server)
smtp.login(smtp_user, smtp_password)
smtp.sendmail(smtp_user, "接收邮件地址2", msg.as_string())
smtp.quit()
send_email('某某某', '每日打卡19', 'superhin@126.com')
每日打卡20-为Pytest添加额外参数和配置项
起止时间: 2020/11/13 09:35 ~ 2020/12/09 23:59
知识点:https://www.jianshu.com/p/9a03984612c1
添加自定义选项和配置
- 新建一个空白的pytest项目,使用conftest.py中def pytest_addoption(config) 钩子方法,为Pytest添加一个命令行参数 --name,添加一个配置项age
- 利用def pytest_terminal_summary(config) 钩子方法,在最后运行时输出 参数 name和age的信息。
如,新建pytest.ini
[pytest]
age = 12
运行pytest --name=kevin
,执行最后打印,我是kevin,年龄12
参考答案
目录中新建conftest.py,内容如下
def pytest_addoption(parser):
"""Pytest初始化时添加选项的方法"""
parser.addoption("--name", help='自定义参数name')
parser.addini('age', help='自定义配置age')
def pytest_terminal_summary(config):
"""Pytest生成报告时的命令行报告运行总结方法"""
name = config.getoption("--name")
age = config.getini('age')
print(f'我是{name},年龄{age}')
运行pytest --name=kevin
后显示
======================== test session starts ===========================
platform darwin -- Python 3.7.7, pytest-5.4.3, py-1.8.1, pluggy-0.13.1
rootdir: /Users/superhin/项目/示例/pytest_demo, inifile: pytest.ini
plugins: metadata-1.10.0, html-2.1.1
collected 0 items
我是kevin,年龄12
=======================no tests ran in 0.01s ============================
每日打卡21-Python操作Excel
起止时间: 2020/11/16 11:40 ~ 2020/12/16 23:59
知识点:https://www.cnblogs.com/superhin/p/11503933.html
- 新建Excel文件数据格式如下
关键词 结果
Python自动化
Selenium
接口测试
Appium
Postman - 使用Selenium+百度 逐个搜索每一个关键词,成功后在结果列写入成功。
- (可选)使用Appium+Baidu App 逐个搜索每一个关键词,成功后在结果列写入成功。
参考答案