Python的selenium自动化测试脚本

本次主题是测试新版的一个web版的数据库客户端,适用于各种主流数据库,测试的目的:主要测试各种sql的执行情况,后根据结果针对性的优化;

总体思路步骤:
1.加载不同数据库类型的测试案例,组成不同list;
2.登录系统,由于无法点击到执行sql页面,所以指定输入网址到达指定页面;
3.清空所有执行窗口,由于系统自动保存执行窗口,所以输入窗口刷新到统一的状态;
4.循环点击数据库标签,打开这个数据库的sql执行窗口,根据数据库类型选择测试案例;
5.循环sql执行:循环测试案例,其中重复操作为—点击编辑框,输入sql,点击运行,获取结果,清空输入的sql,刷新窗口;
6.结果处理:获取的结果和测试案例信息组成新的list,写入结果文件,异常结果截图保存文件目录;
7.循环测试所有的数据源,循环结束,sql结束;

本次开发总结,会在代码块做注释:
1.在有些标签无法点击或者无法获取时,可以绕开点击,如果有固定网址一样可以输入网址到达指定页面;
2.在菜单栏固定区域的标签获取时,可以用循环判断标签内容,后期变动或者修改简单;
3.如果一个功能按钮可能出现在多个标签位置时,可以尝试用try捕捉,但是如果是3个位置时,还未想到办法;
4.对于需要显示等待的事件或者结果,也可以判断反馈结果,如果有结果就跳出while True,如果没有结果循环等待;
5.对于变化的标签时,如果我们每次操作固定,那就可以让变化的便签都刷新成统一状态,这样便于操作,因为我们的目的是测试sql的执行结果,要的是结果而不是这个页面变化而产生的BUG;

# -*- coding: utf-8 -*-

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from lxml.html import fromstring, tostring
from lxml import etree
import time
import xlrd
from xlrd import xldate_as_tuple
import csv
import numpy as np
import os
from datetime import datetime, date, timedelta



def check_executesql(screenshot_path,mysql_testsqllist,oracle_testsqllist,db2_testsqllist):
#在执行平台,查询各个sql结果,有结果的获取结果存入excel文档,异常信息进行截图及获取信息;
    sqllist=[]
    sqltoday=str(time.strftime('%Y-%m-%d-%h:%m',time.localtime()))
    sqlday=str(time.strftime('%Y-%m-%d',time.localtime()))
    # input()
    filename='tablespace-'+sqltoday
    sqllist=["sql",filename]
    driver=webdriver.Chrome()
    # driver=webdriver.Firefox()
    driver.get("http://9.1.10.129:8888/login/")
    #将浏览器最大化显示
    driver.maximize_window()
    # 填充用户名 密码 验证码
    # driver.find_element_by_id("username").send_keys("pengwu")
    # driver.find_element_by_id("password").send_keys("123456")
    
    driver.find_element_by_xpath('//input[@placeholder="请输入用户名"]').send_keys('test')
    driver.find_element_by_xpath('//input[@placeholder="请输入密码"]').send_keys('123456')
    # 点击登录 登陆成功
    # driver.find_element_by_class_name("ant-btn ant-btn-primary ant-btn-lg").click()
    driver.find_element_by_xpath("//button").click()
    time.sleep(2)
    #这个用到1,由于无法点击到目标标签,没辙只能数据网址跳转到指定页面
    driver.get("http://9.1.10.129:8888/hermes/workspace/")
    time.sleep(3)
    # 点击工作台,用循环式,利于后期迁移或修改
    benchs=driver.find_elements_by_xpath('//ul[@role="menubar"]/div')
    for bench in benchs:
        benchvalue=bench.text
        # 所有执行平台标签
        # print('执行平台标签',benchvalue)
        #获取文件
        if '工作台'==benchvalue:
            bench.click()
            time.sleep(2)
            #所有数据源标签名称
            databases=driver.find_elements_by_xpath('//div[@class="tree_3hqfs"]/main/section[1]/div/div')
            print('数据源个数:',len(databases))
            sqllist=[]
            for i in range(1,len(databases)+1):
                # 每次选择新的数据源前,先把输入框全部删除
                # 由于获取编辑框数量随着关闭减少,所以按照标签个数删除异常,所以永远删除标签为3的编辑框,直到编辑框个数为0;
                sqlexecutes=driver.find_elements_by_xpath('//div[@class="ivu-tabs-nav-wrap"]/div/div')
                print('清空sql执行区域个数:',len(sqlexecutes)-2)
                for x in range(len(sqlexecutes)-2):
                    se=driver.find_elements_by_xpath('//div[@class="ivu-tabs-nav-wrap"]/div/div')
                    if len(se)>2:
                        driver.find_element_by_xpath('//div[@class="ivu-tabs-nav-wrap"]/div/div[3]').click()
                        driver.find_element_by_xpath('//div[@class="ivu-tabs-nav-wrap"]/div/div[3]/div/i').click()
                        
                dbname=driver.find_element_by_xpath('//div[@class="tree_3hqfs"]/main[%i]/section[1]//span' % i).text
                print('数据源标签名称:',dbname)
                time.sleep(2)
                driver.current_window_handle
                # 点击数据源,打开编辑框
                driver.find_element_by_xpath('//div[@class="tree_3hqfs"]/main[%s]/section[1]' % i).click()
                # 获取数据库类型
                dbtype=driver.find_elements_by_xpath('//div[@class="ivu-split-vertical"]/div[1]/section/div/section[2]/span')
                typels=[ d.text for d in dbtype]
                print('数据库信息',typels)
                # print(typels[-1].split(":")[-1].strip())
                if typels[-1].split(":")[-1].strip()=='mysql':
                    sqllist=mysql_testsqllist
                elif typels[-1].split(":")[-1].strip()=='oracle':
                    sqllist=oracle_testsqllist
                elif typels[-1].split(":")[-1].strip()=='db2':
                    sqllist=db2_testsqllist
                # 组装结果集文件名称及填充表头
                result_filename=screenshot_path+dbname+'-'+typels[-1].split(":")[-1].strip()+'.csv'
                print('保存结果集文件',result_filename)
                writecsv([sqllist[0]],result_filename)
                for i in range(1,len(sqllist)):
                    resultlist=[]
                    # 点击编辑框
                    driver.find_element_by_xpath('//div[@class="editor_2FiuW"]').click()
                    time.sleep(1)
                    # print('点击编辑框')
                    driver.find_element_by_xpath('//textarea[@autocorrect="false"]').send_keys(sqllist[i][2])
                    time.sleep(1)
                    try:
                        #由于执行按钮动态变化,所以需要设置2个,
                        # print('点击执行执行按钮')
                        driver.find_element_by_xpath('//div[@class="ivu-split-vertical"]/div[1]/section/div/section[1]/span/span/div[2]').click()
                    except Exception as e:
                        driver.find_element_by_xpath('//div[@class="ivu-split-vertical"]/div[1]/section/div/section[1]/div[1]').click()
                    time.sleep(1)
                    try:
                        # 不一定有二次确认的按钮
                        driver.find_element_by_xpath('//div[@role="tooltip" and @x-placement="bottom"]//button[2]').click()
                        time.sleep(2)
                    except Exception as e:
                        print('无二次确认按钮')
                        # pictures=driver.get_screenshot_as_file(screenshot_path+dbname+'-1.png')
                    #循环等待结果,
                    while True:
                        result=driver.find_element_by_xpath('//div[@class="bottom-tab_3KIIY"]/div/div[2]').text
                        if result:
                            result=result.replace("\n",' ').strip()
                            # print(result)
                            #判读关键字截图
                            tr='耗时' in result
                            if not tr:
                                pname=screenshot_path+dbname+'-'+typels[-1].split(":")[-1].strip()+'-'+str(int(sqllist[i][0]))+'.png'
                                print('异常执行结果截图:',pname)
                                pictures=driver.get_screenshot_as_file(pname)
                            break
                    # 填充结果集数据
                    resultlist.append([str(int(sqllist[i][0])),sqllist[i][1],sqllist[i][2],sqllist[i][3],result])
                    print(resultlist)
                    writecsv(resultlist,result_filename)
                    # 清理输入框中的sql信息
                    driver.find_element_by_xpath('//div[@class="editor_2FiuW"]').click()
                    driver.find_elements_by_xpath('//div[@class="ivu-split-vertical"]/div[1]/section/div/section[1]/div')[-2].click()
                    # 刷新获取焦点,清空结果集数据,将输入框刷新成统一状态便于操作
                    driver.refresh()
                    time.sleep(2)
                    #点击数据库的编辑框标签
                    driver.find_element_by_xpath('//div[@class="ivu-tabs-nav-wrap"]/div/div[3]').click()
            break
    print('工作台的所有sql测试完毕,关闭浏览器!')
    driver.quit()


def readexl(filename):
    print('加载测试文件',filename)
    data=xlrd.open_workbook(filename)
    table=data.sheets()[0]
    # print(table.name,table.nrows,table.ncols)
    x=table.nrows
    y=table.ncols
    print('文件行列,rows:',x,',cols:',y)
    # print('标题:',table.cell(0,0).value)
    resultlist=[]
    for i in range(0,x):
        ls=[]
        for j in range(0,y):
            type=table.cell(i,j).ctype
            cell=table.cell_value(i,j)
            # print(cell,end=" ")
            ls.append(cell)
        resultlist.extend([ls])
    return resultlist

def readcsv_data(database_file):
    #读取文件csv格式
    # print('读取文件csv格式,加入数据筛选',database_file)
    # filepath,tempfilename=os.path.split(database_file)
    # filename,extension=os.path.splitext(tempfilename)
    # filename,d=filename.split("-")
    database_list=[]
    #读取文件,获取id和系统名称字典
    # print('读取文件',database_file,'\n')
    with open(database_file,encoding='utf-8') as csvfile:
        csv_reader = csv.reader(csvfile)  # 使用csv.reader读取csvfile中的文件
        birth_header = next(csv_reader)  # 读取第一行每一列的标题
        for row in csv_reader:  # 将csv 文件中的数据保存到birth_data中
            database_list.append(row)
    print('读取文件',database_file,'获取数据文件行数:',len(database_list),'\n')
    return database_list

def writecsv(lists,file):
#写入到csv文件中
    with open(file,'a+', newline='') as csvwrite:
        csv_write=csv.writer(csvwrite)
        for ls in lists:
            csv_write.writerow(ls)

def main():
    # 截图保存位置
    screenshot_path='D:\\SQLEXECUTE\\'
    #获取各种数据库测试样例
    mysql_testfile='D:\\SQLEXECUTE\\mysql测试样例.xlsx'
    oracle_testfile='D:\\SQLEXECUTE\\oracle测试样例.xlsx'
    db2_testfile='D:\\SQLEXECUTE\\db2测试样例.xlsx'
    
    mysql_testsqllist=readexl(mysql_testfile)
    oracle_testsqllist=readexl(oracle_testfile)
    db2_testsqllist=readexl(db2_testfile)
    check_executesql(screenshot_path,mysql_testsqllist,oracle_testsqllist,db2_testsqllist)

if __name__=='__main__':
    main()