写在前面的:
最近在学习写UI自动化脚本,并且正在自己的项目中实践。
在断言的时候使用了try语句块,其实自己只是对python对try语句有基本的语法认识,根本不知道怎么去用。结果出了个bug,老半天才找出原因来。是因为自己把好多代码都try了,结果有错误也没曝露出来,很是忧桑。当时有点怀疑人生了,为什么没报错,但是却没有得到自己预想的结果呢?
有问题的代码类似下面这样(当时的代码找不到了,下面代码只是说明下错误):
#coding=utf-8
def div(a,b):
try:
result=a/b
return result
except Exception as e:
print("不知道发生了什么。。")
if __name__=="__main__":
print(div(1,0))
第一个禁忌:避免捕捉所有异常并吞噬它们。
看上去好像在处理异常的时候也打印了一句“不知道发生了什么”,但其实这里是把0作为除数的这种错误给吞噬了,也就是说,代码没有对这种错误情况给出一个原因或者针对性对解决方案。
这种情况的解决办法:
def div1(a,b):
"""
:param a: int或者float类型
:param b: int或者float类型
:return: 如果a,b不是int或者foat类型,返回None;如果b=0,返回None;否则返回a/b的结果
"""
if not isinstance(a,(int,float)) :
return None
if not isinstance(b,(int,float)):
return None
if 0==b:
return None
result=a/b
return result
这种方法就是对于一个传进来的参数做这种校验,因为你不知道别人会传什么样的参数,所以要想到各种情况。
如果有的时候,代码中很多地方都可能会发生异常,类型可预见,但是什么时候发生不知道,这就要用TryExcept语句。比如webdriver的元素操作,有时候是元素的获取有问题,有时候是操作元素的时候有问题,常见都就是报错ElementNotVisibleException、NoSuchElementException,还有一些其他的异常(如下图)。
![](https://img.haomeiwen.com/i16437405/978414a0395cef48.png)
你在做页面操作的时候可能是一系列操作,那每个元素都要判断是否正常返回嘛?那估计是要累死宝宝了。这时候用TryExcept就很方便。我是这样处理的:
1.所有元素获取和操作的部分try
2.如果except到了异常,根据异常的种类做不同的处理。ElementNotVisibleException、NoSuchElementException这两种异常是我最常遇见的,原因就是脚本不太稳定,大多数情况下是可以通过用例的,少数情况会报这两个异常,那么我希望遇到这两种异常的时候重新跑用例脚本,代码不会因此而中断,增强了代码的健壮性。遇到未知的Exception,此时一定要raise出来,raise之前也可以自己打点日志。
3.对于webdriver的assert方法也要try一下。因为assert方法只要失败,就会抛AssertionError(感觉这种处理方式不是很好,但是又必须用selenium,通常情况应该是正确或者错误都需要返回一个值,而不是抛出一个异常呀)。except语句块主要是记录下测试用例失败的数据,然后也要raise掉。如果不raise掉,那么这个用例结果会是成功(但实际是失败了,只不过你但try让它看起来成功了)。代码如下:
def testToolButton(self):
logger.info("开始执行工具按钮的测试脚本...")
try:
# 设置浏览器为iphone模式打开
mobile_emulation = {'deviceName': 'Galaxy S5'}
options = webdriver.ChromeOptions()
options.add_experimental_option("mobileEmulation", mobile_emulation)
browser = webdriver.Chrome(chrome_options=options)
browser.get("https://plogin.m.jd.com/user/login.action")
logger.info('启动浏览器,访问"登录"页面...')
LoginAction.login('13180314708','TGB6yhn',browser,'http://test-jdread.jd.com/h5/m/')
logger.info('登录完成...')
time.sleep(3)
logger.info('创建书城页面,点击工具按钮,点击"我的"按钮...')
bookCityPage = BookCityPage(browser)
bookCityPage.toolButtonObj().click()
bookCityPage.minePageButtonObj().click()
time.sleep(3)
minePage=MinePage(browser)
try:
minePage.ExitButtonObj()#如果在"我的"页面找到退出按钮,则通过测试用例,如果没找到该按钮则测试用例未通过
self.assertTrue(1==1)
logger.info('在"我的"页面找【退出】按钮,成功,用例通过')
except AssertionError as e:
logger.debug('在"我的"页面找到【退出】按钮,失败,用例不通过')
raise e
except ElementNotVisibleException as e:
logger.error("元素不可见..")
except NoSuchElementException as e:
logger.error("元素没有找到..")
except Exception as e:
logger.error(e)
raise e
另外python允许程序员自己写一些异常类,这时候要注意的就是禁忌二:抛出的异常应该解释为什么,不能让别人来猜测,你可能注意到,selenium定义类很多异常,但是人家还是把异常信息的原因返回的很清楚。同理,我们自己在写异常的时候,抛出时也要写清楚为什么,方便别人在用的时候不知道发生了什么就抛出了异常。以下代码是一个自己定义异常的例子,ShortInputException类里面有两个成员变量length和atleast,分别代表输入的长度和要求最短的长度,一旦length小于atleast就应该抛出异常。这两个成员变量就能说明异常的原因:
#coding=utf-8
import sys
class ShortInputException(Exception):
def __init__(self,length,atleast):
Exception.__init__(self)
self.length=length
self.atleast=atleast
def assertInputLength(atleastLen):
s=input("请输入一个长度大于%s的字符串:"%str(atleastLen))
if len(s)<atleastLen:
raise ShortInputException(len(s),atleastLen)
else:
return True
try:
assertInputLength(3)
except ShortInputException as e:
sys.stderr.write("%s is not enough long ,atleast %s \n"%(e.length,e.atleast))
禁忌三:不要使用异常来控制进程,这样您的程序就很难理解和维护
虽然python是一种很“自由”的语言,tryExcept能做很多事情,笔者自己就写过这样的代码:
#coding=utf-8
import time
def AIclick(element):
for i in range(60): # 循环60次,从0至59
if i >= 59: # 当i大于等于59时,打印提示时间超时
print("timeout")
break
try: # try代码块中出现找不到特定元素的异常会执行except中的代码
element.click()
except: # 上面try代码块中出现异常,except中的代码会执行打印提示会继续尝试查找特定的元素id
print("wait for find element")
else:
break
time.sleep(1)
以上代码是为了稳定的获取页面元素。但不得不说,这样的代码很难让人理解。代码逻辑简单还好,一旦复杂,就会跳进自己挖的坑里。
禁忌四:如果有需要,记得使用最后释放资源
这里使用java来说明下,最有名的就是数据库的操作:
package com.runoob.test;
import java.sql.*;
public class MySQLDemo {
// MySQL 8.0 以下版本 - JDBC 驱动名及数据库 URL
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost:3306/RUNOOB";
// MySQL 8.0 以上版本 - JDBC 驱动名及数据库 URL
//static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";
//static final String DB_URL = "jdbc:mysql://localhost:3306/RUNOOB?useSSL=false&serverTimezone=UTC";
// 数据库的用户名与密码,需要根据自己的设置
static final String USER = "root";
static final String PASS = "123456";
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try{
// 注册 JDBC 驱动
Class.forName(JDBC_DRIVER);
// 打开链接
System.out.println("连接数据库...");
conn = DriverManager.getConnection(DB_URL,USER,PASS);
// 执行查询
System.out.println(" 实例化Statement对象...");
stmt = conn.createStatement();
String sql;
sql = "SELECT id, name, url FROM websites";
ResultSet rs = stmt.executeQuery(sql);
// 展开结果集数据库
while(rs.next()){
// 通过字段检索
int id = rs.getInt("id");
String name = rs.getString("name");
String url = rs.getString("url");
// 输出数据
System.out.print("ID: " + id);
System.out.print(", 站点名称: " + name);
System.out.print(", 站点 URL: " + url);
System.out.print("\n");
}
// 完成后关闭
rs.close();
stmt.close();
conn.close();
}catch(SQLException se){
// 处理 JDBC 错误
se.printStackTrace();
}catch(Exception e){
// 处理 Class.forName 错误
e.printStackTrace();
}finally{
// 关闭资源
try{
if(stmt!=null) stmt.close();
}catch(SQLException se2){
}// 什么都不做
try{
if(conn!=null) conn.close();
}catch(SQLException se){
se.printStackTrace();
}
}
System.out.println("Goodbye!");
}
}
禁忌五:处理异常后不要忘记清理或回滚
这里还是用java的数据库事务操作来说明下:
public boolean DeleteSeatInfo(Seat seat){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
String sql="UPDATE info_seat seat,info_classroom room SET seat.seat_student_name = ? ,seat.seat_empty = ? WHERE seat.classroom_id = room.classroom_id AND seat_id = ?";
try{
conn = DbUtils.getConnection();
conn.setAutoCommit(false);
ps = conn.prepareStatement(sql);
System.out.println(sql);
ps.setString(1,seat.getSeatName());
ps.setInt(2, seat.getIsEmpty());
ps.setString(3, seat.getSeatId());
//ps.addBatch();
//ps.executeBatch(); //批量执行
ps.executeUpdate();
conn.commit();//提交事务
return true;
}catch(SQLException e){
try {
conn.rollback(); //进行事务回滚
} catch (SQLException ex) {
}
}finally {
DbUtils.close(conn, ps, rs);
}
return false;
}
文章参考以下大牛的:
http://news.51cto.com/art/201801/565741.htm
https://www.cnblogs.com/cnkemi/p/8985654.html
http://www.runoob.com/java/java-mysql-connect.html
https://blog.csdn.net/yy763496668/article/details/51488882
网友评论