作者: 李镇伟

将Jira获取的UTC时间转本地时间yyyy-MM-dd’T’HH:mm:ss

通过API获得jira 备注(comment)的更新时间,发现获得的UTC时间格式为
2018-08-14T02:52:22.216+0000
这个时候,如果需要转换成datetime :yyyy-MM-dd'T'HH:mm:ss格式,
或者date:yyyy-MM-dd,可以用下面的代码:

#!/usr/bin/python
# -*- coding:utf-8 -*-
import re
import time
from datetime import datetime
def utc2local(utc_st):
    # UTC时间转本地时间(+8:00)
    now_stamp = time.time()
    local_time = datetime.fromtimestamp(now_stamp)
    utc_time = datetime.utcfromtimestamp(now_stamp)
    offset = local_time - utc_time
    local_st = utc_st + offset
    return local_st
def local2utc(local_st):
    # 本地时间转UTC时间(-8:00)
    time_struct = time.mktime(local_st.timetuple())
    utc_st = datetime.utcfromtimestamp(time_struct)
    return utc_st
def utc_format(utcstr):
    """
    将不标准的utc时间字符串转换成datetime格式
    :param utcstr: 不标准的utc字符串,如‘2018-04-23T03:43:35.000+0000’
    :return:
    """
    utcstr = re.sub('.ddd+0000', '', utcstr, count=1)
    try:
        UTC_FORMAT = "%Y-%m-%dT%H:%M:%S"
        d_time = datetime.strptime(utcstr, UTC_FORMAT)
    except Exception:
        UTC_FORMAT = "%Y-%m-%d %H:%M:%S"
        d_time = datetime.strptime(utcstr, UTC_FORMAT)
    return d_time
def get_local_date(utcstr):
    """
    将jira返回的str类型的时间,转换成datetime格式,并去除日期之后的内容(提供给SprintDailyIssuesBar使用的日期处理接口)
    :param utcstr: jira返回的time字符串
    :return:datetime格式
    """
    utc_st = utc_format(utcstr)
    # print utc_st
    local_st = utc2local(utc_st)
    # print local_st
    local_str = local_st.strftime('%Y-%m-%d')
    local_date = datetime.strptime(local_str, "%Y-%m-%d")
    # print local_date
    return local_date
def get_local_datetime_str(utcstr):
    """
    将jira返回的str类型的UTC时间,转换成str类型的local time,包含具体的时、分、秒(提供给tableData使用,转换时间)
    :param utcstr: jira返回的time字符串
    :return:string
    """
    utc_st = utc_format(utcstr)
    # print utc_st
    local_st = utc2local(utc_st)
    # print local_st
    local_str = local_st.strftime('%Y-%m-%d %H:%M:%S')
    return local_str
 if __name__ == '__main__':
     utcstr1 = '2018-08-14T02:52:22.216+0000'
     tt=get_local_datetime_str(utcstr1)
     print(tt)

图解OWASP ZAP录制登录请求,并且进行SQL注入测试

好了,废话不多说,下面几张图请按顺序观看,是作者用OWASP ZAP对某一个网站进行登录的SQL注入测试,网址和登录密码,作者已经打码了,如有侵权或引起其他不适,请联系作者。
——————————————第一步——————————————

——————————————第二步——————————————

——————————————第三步——————————————
——————————————第四步——————————————
——————————————第五步——————————————

 

记录一次我做的influxDB性能测试

InfluxDB性能测试报告
被测环境:
腾讯云

CPU 内存 带宽 版本号
4核 16G 1Gbit/s Ubuntu 4.8.4-2ubuntu1~14.04.3

 
地址:
 
被测程序:
Docker下安装的influxDB 端口8086
 
压测环境:
腾讯云

CPU 内存 带宽 版本号
2核 8G 1Gbit/s Ubuntu 4.8.4-2ubuntu1~14.04.3

 
地址:
 
测试程序:
从github上找的influxdata公司提供的两款测试工具
influx-stress 用于写入测试
influxdb-comparisons用于查询测试
 
测试场景:

写入测试
工具名称 influx-stress
 
工具github地址 https://github.com/influxdata/influx-stress
测试原理 该工具是通过go语言的fasthttp库编写的。
1.     会在服务器上创建一个数据库stress
2.     然后创建一个MEASUREMENT(类似关系数据库的表)名为ctr
该表有time,n.some三个字段
3.     不断的向stress数据库的ctr表插入数据,每次插入的数据都包含三个字段。每一条数据称为一个points。
插入数据的方法是通过influxDB的HTTP API 发送请求(POST /write?db=stress)
 
测试命令 influx-stress insert -r 60s –strict –pps 200000 –host http://10.XX.XX.XX:8086
 
测试程序运行结果
Points Per Second(发起请求) Write Throughput(points/s)
(数据库实际处理结果)
CPU平均利用率
200000 199713 33%
300000 299280 45%
400000 392873 62%
500000 491135 80%
600000 593542 90%
650000 606036 93%
700000 613791 95%

测试结论:最大的吞吐量为每秒写入60万条数据。这之后,每秒发送的points再多,吞吐量也不会增加,同时CPU利用率已达90%。
 

查询测试
工具名称 influxdb-comparisons
 
工具github地址 https://github.com/influxdata/influxdb-comparisons
测试原理 该工具是通过go语言的fasthttp库编写的。
1.     会在服务器上创建一个数据库benchmark_db
2.     然后创建9个MEASUREMENT :cpu,disk,diskio,kernel,mem,net,nginx,postgresl
每个measurement 有2160行数据。
3.     通过http GET请求”GET /query?db=benchmark_db“查询cpu这张表。
查询语句为:SELECT max(usage_user) from cpu where (hostname = ‘host_0’) and time >= ‘2016-01-01T01:16:32Z’ and time < ‘2016-01-01T02:16:32Z’ group by time(1m)
可以取出61条数据。
 
测试命令 ./bulk_query_gen -query-type “1-host-1-hr” | ./query_benchmarker_influxdb -urls http://10.XX.XX.XX:8086 -limit 1000
 
测试程序运行结果
查询命令执行次数
(-limit)
命令最短执行时间 每条命令平均执行时间 命令最大执行时间 总耗时
100 1.20ms 1.69ms 4.36ms 0.2sec
200 1.20ms 1.71ms 7.40ms 0.3sec
300 1.25ms 1.73ms 7.54ms 0.5sec
400 1.21ms 1.71ms 7.54ms 0.7sec
500 1.20ms 1.70ms 7.54ms 0.8sec
600 1.17ms 1.67ms 7.54ms 1.0sec
700 1.14ms 1.66ms 8.33ms 1.2sec
800 1.14ms 1.65ms 8.33ms 1.3sec
900 1.14ms 1.63ms 8.33ms 1.5sec
1000 1.14ms 1.64ms 8.33ms 1.6sec

测试结论:因为该工具最大只能测到读取1000条数据,所以没有继续加大压力测试。查询操作的消耗时间因为受到被查询表的数据量和查询语句的复杂性影响,所以在influxDate官方给出的被查表和查询语句下,算出来是平均每秒执行600次查询。
 
 
 
 

wxpython 的pub/sub发布订阅,夸窗口传递信息

我们经常会用到两个窗口之间互相传递信息,或者把一个窗口里,文本框输入的值,传递到另外一个frame的另外一个文本框里,
这个时候,我们可以使用wxpython的pub/sub方法。
test.py


class Main(wx.App):
          def __init__(self):
                  #mainFrame 是用wxglade画的一个frame导出来的python文件
                   self.frame1 = mainFrame.MyFrame(None, wx.ID_ANY, "")
                   self.frame1.Show()
                   self.children=test2.children()
                  #定义一个name1属性,值来自于窗口上的文本框控件
                   self.name1 = self.frame1.text_ctrl_1
                  #  离开焦点,就把fram1的文本框里的值传入children的另外一个文本框内
                  self.name1 .Bind(wx.EVT_COMMAND_KILL_FOCUS, self.save)
                  #发布一个叫in_sync的主题,触发sync函数
                   pub.subscribe(self.sync, "in_sync")
          def sync(self, msg):
                  #给children的text_ctrl_2字段设置值
                  self.children.frame.text_ctrl_2.SetValue(msg)
          def save(self,evt):
                    value=self.name1.GetValue()
                    #调用一下in_sync方法,如果in_sync方法在别的class里,
                    #也是可以调用pub.sendMessage成功的
                    self.children.in_sync(value)

test2.py

class children(wx.App):
           def __init__(self):
                  #mainFrame 是用wxglade画的一个frame导出来的python文件
                   self.frame = mainFrame.MyFrame(None, wx.ID_ANY, "")
                   self.frame.Show()
           def in_sync(text):
                 #订阅"in_sync"主题
                 wx.CallAfter(pub.sendMessage, "in_sync", msg=text)

wxpython 根据item的名字找到tree_ctrl对应的节点

业务场景,需要在一个tree_ctrl中找到一个节点,并且给该节点添加子节点

def get_item_by_label(self, tree, search_text, root_item):
    item, cookie = tree.GetFirstChild(root_item)
    while item.IsOk():
        text = tree.GetItemText(item)
        if text.lower() == search_text.lower():
            return item
        if tree.ItemHasChildren(item):
            match = self.get_item_by_label(tree, search_text, item)
            if match.IsOk():
                return match
        item, cookie = tree.GetNextChild(root_item, cookie)
    return wx.TreeItemId()
result = get_item_by_label(tree, '已有节点', tree.GetRootItem())
if result.IsOk():
    print('We have a match!')
    new_item = tree.AppendItem(result , "新节点")

wxpython一个event,多个handler,先绑定的后执行

Q:为什么会有一次event,多次handler的需求呢?
A:因为wxpython 目前有一个BUG,在一个event函数中,无法使用两次Dialog.showModal方法。当第一个Dialog消失后,第二个dialog再弹出,会造成系统卡死。所以必须把两个dialog.showModal放入两个event中。
绑定规则:先绑定的后执行
给按钮sync_button 绑定三个事件,第一次执行的在下,第二次执行的在上,如下:
给按钮绑定事件

        self.sync_button.Bind(wx.EVT_BUTTON, self.sync_three_event)
        self.sync_button.Bind(wx.EVT_BUTTON, self.sync_two_event)
        self.sync_button.Bind(wx.EVT_BUTTON, self.sync_one_event)

编写事件的event

    def sync_one_event(self, event):
        print("第一次触发事件" )
        event.Skip()
    def sync_two_event(self, event):
        print("第二次触发事件" )
        event.Skip()
    def sync_three_event(self, event):
        print("第三次触发事件" )
        event.Skip()

最后触发的结果是:

第一次触发事件
第二次触发事件
第三次触发事件

实现wxpython的拖拽上传功能

实现wxpython的拖拽上传功能
1.从FileDropTarget继承

···
class MyFileDropTarget(wx.FileDropTarget):
def init(self):
wx.FileDropTarget.init(self)
def OnDropFiles(self, x, y, filepath):
# file=os.path.basename(filepath[0])
#各种文件操作之类的
return False ···

2.给panel绑定拖拽功能

    filedrop = MyFileDropTarget()
    self.frame.panel_2.SetDropTarget(filedrop)

3.注意事项:
一次只能拖拽一个文件上去,如果一次拖拽A,B,C三个文件。最后filepath只能获取到A文件,B,C丢失

python2.7-wxpython4.0.1-pyinstaller打包的正确用法

hi 各位看官,本文是截止2018年7月4日,基于python2.7开发wxpython时的打包正确用法。(2018年11月16日,发现pyinstall 3.4版本解决了这个问题了)
坑1:
wxpython的最新版本为4.0.3 但是因为4.0.3引入了新的pubsub 4.0 不兼容python2.7。所以如果用python2.7的同学必须降级到wxpython 4.0.1版本
坑2:
打包方面,我们可以用pyinstaller打包。但是pip上,pyinstaller最新的版本是3.3.1 版本,此版本上有bug https://github.com/pyinstaller/pyinstaller/issues/2215
但是后期,pyinstall的作者修复了此bug在dev版本上,所以我们只有去github上 安装最新的dev版本才能打包成功
踩坑表现:

Traceback (most recent call last):
  File "<string>", line 41, in <module>
  File "<string>", line 36, in walk_packages
  File "<string>", line 20, in walk_packages
  File "d:program filespythonpython27libsite-packageswx-3.0-mswwxlibpubsubcorearg1__init__.py", line 16, in <module>
    raise RuntimeError(msg)
RuntimeError: Should not import this directly, used by pubsub.core if applicable

出现pubsub报错,因为wxpython 2.8.0之前的版本用的是corearg1 所以为了能顺利打包,我们要进行如下操作:
填坑方法:
1.pip装wxpython 4.0.1版本

pip install wxpython==4.0.1

2.pip装dev版本pyinstaller

 pip install git+https://github.com/pyinstaller/pyinstaller

3.重新打包主入口main.py

pyinstaller -D main.py

wxpython之tree_ctrl图标与点击事件


如何用wxpython展示一个如图所示的的树呢?
wxpython的treectrl功能可以实现。
1.首先我们可以用wxglade画前端frame界面,添加一个TreeCtrl ,或者多个TreeCtrl,添加root.每一个tree只能有一个root

self.tree_ctrl_1 = wx.TreeCtrl(self.panel_1, wx.ID_ANY)
my_defect_root =self.my_tree.AddRoot("My Defect")

2.然后给设置图片list 用来给根节点和树节点放不一样的图片

self.image_list = wx.ImageList(16, 16)
self.case_icon =self.image_list.Add(wx.Image("resource/image/icon/bookmark.png",
                                              wx.BITMAP_TYPE_PNG).Scale(16, 16).ConvertToBitmap())
self.folder_icon =self.image_list.Add(wx.Image("resource/image/icon/document.png",
                                                wx.BITMAP_TYPE_PNG).Scale(16, 16).ConvertToBitmap())

3.然后给树设置imagelist

self.project_tree.SetImageList(self.image_list)

#注意千万不能用AssignImageList ,这样写虽然运行没问题,但是关闭窗口时会导致crash,一定要用SetImageList
4.给每一个节点设置图片和文字

status_node =self.my_tree.AppendItem(my_defect_root, “open”)
self.my_tree.SetItemImage(status_node, self.folder_icon, wx.TreeItemIcon_Normal)

5.给节点绑定双击事件

self.my_tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.show_defect_info)
def show_defect_info(self, event):
    item = event.GetItem()
    self.my_tree.Expand(item)
    print(self.my_tree.GetItemText(item))

自动化测试也是得有需求文档的!

很多时候,我们只注意在了自动化的实现上,而忘记了对自动化测试的需求分析上,从而导致后期做出来的目录结构不合适,改动困难,debug难度高,自动化测试的首要特性就是重复执行,不能重复执行,且易暴露问题的自动化,不如手工测试。
我认为的自动化测试框架必有以下特征:
1.方便指定待测集合(用例、套件)运行
2.方便失败之后的debug过程
3.方便编写(建议用强类型语言)
4.与手工测试能互相呼应,能够很容易明白这条用例要做什么。就好比我们说英语一样,我不想要收到了英语的信息之后,通过翻译软件,告知我的大脑它的中文意思,然后我的大脑再在我的记忆之中找出对应的英语单词,最后由我的口发出。我希望是,我接受到了英语信息,我的大脑不需要把英文翻译成中文,而是直接明白英文的意思,并且用英文发出,整个过程不涉及翻译,所以建议不要搞花式的EXCEL关键字驱动或者其他形式的XX驱动,有这么一句话,show me the code
手工测试用例->自动化测试用例需求分析
我们在最初开发自动化测试框架的时候,一定要记住结合目前已有的手工用例,并且分析,最后定出我们自动化测试框架的结构,不然后期会吃大亏,就像产品人员根据已有竞品,分析出我们的开发需求一样,我们得分析我们的手工用例,定义我们的自动化测试框架需求。
这是一条常见的购物测试手工用例:
1.登录网站首页
2.输入用户名,密码
3.购买一款价值500元的产品
4.检查实际支付金额是否正确
期望结果:
实际支付金额正确。
用例看起来很简单。好像操作步骤也不复杂,应该是一条很容易自动化的测试用例。实际不然,我们在做自动化测试的时候,需要进行详细的分析。按照我们自动化的测试逻辑,分为@测试前(数据准备),@测试过程(执行测试步骤)@测试后(用例失败或成功的数据清理)。这条用例按照自动化的逻辑就变成:
@测试前
1.通过数据库插入语句生成一个用户,“用户名”“密码”固定,传入测试数据
2.通过数据库修改语句修改某产品的价格为500,返回“产品名”,传入测试数据
3.通过数据库修改语句修改已知的用户的账户余额大于500
@测试步骤
1.打开网站首页。在网站首页点击登录按钮
2.在登录弹出层输入“用户名”“密码”,按下确定按钮
3.在搜索框输入测试数据中的“产品名”点击搜索按钮
4.在搜索结果列表页点击第一个搜索结果的标题栏
5.在产品详情页点击加入购物车按钮,点击页面右上角的购物车按钮
6.在购物车页面点击立即结算按钮
7.在支付页面选择账户余额支付,完成支付
8.等待页面挑战提示支付成功字样
9.点击页面右上角会员中心按钮
10.在会员中心检查该订单的实际支付金额。获得订单号,传入测试数据
断言:实际支付金额==500
@测试后
1.根据用户名删除该用户
2.判断是否存在订单号,存在则删除订单表中的该订单数据
3.修改购买的该产品的价格为原价
所以,根据上面的用例,我们可以分析出,我们的测试框架有
–测试用例
–测试页面库
–测试动作库
–测试结果校验库
–组合测试步骤库
–其他工具库(包含数据库操作,API操作等)
流程是:
在通过某些工具准备好数据之后,每一个测试用例包含多个测试步骤。每一个测试步骤会在一个测试页面上进行多次动作。操作多个页面元素。操作完成后会经过一到多个测试结果检查。完成测试结果之后,告诉测试基类这次测试的结果是正确还是错误,记录在日志之中。最后在完成所有测试集合之后,最后统一通过邮件发送出来。
在明确业务流程后,完成自动化测试就是具体的落实工作。如果后续有时间,我会发出我推荐的自动化测试代码框架。
我的建议是用testng+ selenide+ ExtentReports+jenkins+maven+jdbc+httpclient这些工具做自动化应该是比较好的,对于mybatis ,dom4j ,poi这些我感觉是用不到的,我曾经加入过excle,xml等一些貌似很合情合理的功能,但是后续在用例失败debug的时候简直让我爆炸,自动化测试,一切从简,能用代码解决的,就不要用其他工具。


苏ICP备18047533号-1