知識)
一、接口自動化requests1.核心內(nèi)容1.get請求參數(shù) 使用 params,參數(shù)拼接在url后面多用于查詢2.post請求json格式 使用 json; 表單格式 使用 data;3.response對象的常用屬性status_code ,json(),text,headers,cookies4.異常處理接口 返回的不是json的時候使用 .json()會報錯誤.案例get請求import requests url http://xxx/api/user/list payload {page:1,pageSize:10} resp requests.get(urlurl, paramspayload) print(resp.status_code) # 響應(yīng)狀態(tài)碼 200 print(resp.json()) # 將響應(yīng)轉(zhuǎn)為字典 # 簡單響應(yīng)斷言 assert resp.status_code 200 assert resp.json()[code] 0params參數(shù)說明params是GET 請求的查詢參數(shù)格式Python 字典 dict。requests 會自動把字典拼接成 URL 后面?key1val1key2val2這一段。(1幾種常見的寫法普通字典params {page:0,pageSize:10} requests.get(url, paramsparams)值為數(shù)字、字符串、Noneparams { name: 測試, age: 20, beginDate: None }同一個key多個值有些接口需要?id1id2id3案例 post-json請求常用import requests url http://xxx/api/login headers {Content?Type:application/json} body {username:zxj,password:123456} resp requests.post(urlurl, jsonbody, headersheaders) token resp.json()[data][token] print(token)二、pytest核心用例命名規(guī)則文件test_xxx.py函數(shù)def test_xx()類TestXxx方法test_xxx()執(zhí)行參數(shù)?v詳細(xì)輸出、?s打印 print、?x失敗停止fixture前置后置scope 作用域yield 實現(xiàn)前置 后置清理返回數(shù)據(jù)給用例pytest.mark.parametrize數(shù)據(jù)驅(qū)動allure 報告基礎(chǔ)使用案例fixture基礎(chǔ)。scopesession全局登錄 獲取 tokenimport pytest import requests pytest.fixture(scopesession) def get_token(): session級別整個測試只登錄一次 url http://xxx/api/login data {username:zxj,password:123456} r requests.post(url,jsondata) # 提取token token r.json()[data][token] yield token # 返回給測試用例yield前面前置 # yield之后全部用例執(zhí)行完成之后執(zhí)行后置這里無需清理 # 測試用例直接接收fixture返回值 def test_query_list(get_token): headers {token:get_token} resp requests.get(http://xxx/api/list,headersheaders) assert resp.json()[code] 0scope取值function默認(rèn)每個用例執(zhí)行一次class每個測試類執(zhí)行一次module每個 py 文件執(zhí)行一次session整個測試會話執(zhí)行一次登錄、數(shù)據(jù)庫連接首選案例parametrize數(shù)據(jù)驅(qū)動同一套邏輯多組輸入生成多條用例測試代碼和數(shù)據(jù)分離import pytest # 參數(shù)參數(shù)名字多組測試數(shù)據(jù) pytest.mark.parametrize(page,expect_code, [(0,-1), (1,0), (-1,-1)]) def test_page_param(page,expect_code,get_token): headers {token:get_token} params {page:page,pageSize:10} res requests.get(http://xxx/api/log,headersheaders,paramsparams) assert res.json()[code] expect_code三、pymysql數(shù)據(jù)庫操作數(shù)據(jù)庫斷言連接數(shù)據(jù)庫、創(chuàng)建游標(biāo)DictCursor查詢結(jié)果返回字典可以按字段名取值寫斷言必備execute 執(zhí)行 sql占位符%s防止 sql 注入fetchone () 取一條fetchall () 取全部增刪改必須conn.commit()異常conn.rollback()回滾fixture封裝全局?jǐn)?shù)據(jù)庫連接不要每個用例反復(fù)打開關(guān)閉數(shù)據(jù)庫斷言場景新增 / 修改接口調(diào)用后查詢數(shù)據(jù)庫校驗數(shù)據(jù)是否落庫正確重點接口返回成功不等于數(shù)據(jù)庫落庫正確必須數(shù)據(jù)庫斷言案例session fixture封裝數(shù)據(jù)庫連接import pymysql import pytest pytest.fixture(scopesession) def get_db(): # 建立連接 conn pymysql.connect( host127.0.0.1, port3306, userroot, password123456, databasetest_command_log, charsetutf8mb4 ) # DictCursor 返回字典格式 cur conn.cursor(cursorpymysql.cursors.DictCursor) yield conn, cur # 給用例返回連接、游標(biāo) # 后置全部用例跑完關(guān)閉資源 cur.close() conn.close()開啟cursorpymysql.cursors.DictCursor查詢返回字典 / 字典列表fetchone()返回1 條字典key 數(shù)據(jù)庫字段名value 字段值沒有數(shù)據(jù)返回Nonefetchall()返回列表里面每一項是一條記錄字典cur.execute(select commandName,commandValue from command_log limit 1) row cur.fetchone() print(row) #輸出 {commandName: 顯示設(shè)置, commandValue: standard} rows cur.fetchall() print(rows) #輸出 [ {commandName: 顯示設(shè)置, commandValue: standard}, {commandName: 亮度設(shè)置, commandValue:high} ]普通 Cursor 不寫 DictCursor返回元組(顯示設(shè)置,standard)不知道哪個值對應(yīng)哪個字段寫斷言很麻煩。案例數(shù)據(jù)庫斷言場景調(diào)用新增命令日志接口接口返回成功查詢數(shù)據(jù)庫校驗數(shù)據(jù)是否正確存入# get_token 和 get_db是 fixture def test_add_command_log(get_token, get_db): # 獲取連接和游標(biāo) conn, cur get_db # token headers {token:get_token} # 1、調(diào)用新增接口 req_body {commandName:顯示設(shè)置,commandValue:standard} resp requests.post(http://xxx/api/addLog,headersheaders,jsonreq_body) assert resp.json()[code] 0 # 2、數(shù)據(jù)庫斷言查詢剛剛插入的數(shù)據(jù) sql select commandName,commandValue from command_log where commandName%s order by create_time desc limit 1 cur.execute(sql, (顯示設(shè)置,)) # %s占位傳參防止SQL注入 db_result cur.fetchone() # 獲取第一條結(jié)果字典格式 # 3、斷言數(shù)據(jù)庫的值和請求入?yún)⒁恢?assert db_result[commandName] 顯示設(shè)置 assert db_result[commandValue] standard注意事項不要字符串拼接 SQL用%s占位傳參防止 SQL 注入insert/update/delete 操作后需要conn.commit()異常場景conn.rollback()回滾自動化盡量清理測試臟數(shù)據(jù)避免臟數(shù)據(jù)導(dǎo)致用例失敗注意點詳解11、禁止字符串拼接 SQL使用%s占位符防止 SQL 注入?錯誤字符串拼接有注入風(fēng)險# 千萬不要這么寫 name xxx sql fselect * from command_log where commandName {name} cur.execute(sql)?正確寫法SQL 語句寫%s占位execute 第二個參數(shù)傳元組把值傳入sql select * from command_log where commandName %s cur.execute(sql, (顯示設(shè)置,)) # 參數(shù)放在元組里多個參數(shù)就 (a,b) res cur.fetchone()注意這里的%s不是 python 字符串格式化是pymysql 的占位符不管字段是字符串、數(shù)字統(tǒng)一用%s。多個參數(shù)案例sql select * from command_log where commandName%s and commandValue%s cur.execute(sql, (顯示設(shè)置,standard))2insert /update/delete必須 commit異常要 rollback () 回滾conn.commit()提交事務(wù)數(shù)據(jù)才真正寫入數(shù)據(jù)庫如果程序發(fā)生異常執(zhí)行conn.rollback()回滾撤銷本次修改案例import pymysql conn pymysql.connect(host127.0.0.1,port3306,userroot,password123456,databasetest_command_log,charsetutf8mb4) cur conn.cursor(cursorpymysql.cursors.DictCursor) try: # insert操作 insert_sql insert into command_log(commandName,commandValue) values(%s,%s) cur.execute(insert_sql, (日志測試,test001)) conn.commit() # ?DML操作一定要commit數(shù)據(jù)落庫 print(插入成功) except Exception as e: print(執(zhí)行出錯,e) conn.rollback() # ?出錯回滾放棄本次修改 finally: cur.close() conn.close()3清理臟 數(shù)據(jù)自動化測試產(chǎn)生的測試數(shù)據(jù)如果不刪除會堆積在庫中影響后面用例執(zhí)行。 兩種方案用例執(zhí)行完主動 delete 刪除本次產(chǎn)生的數(shù)據(jù)推薦使用事務(wù)用例執(zhí)行完直接 rollback不提交測試數(shù)據(jù)根本不會保存適合純測試案例接口自動化用例結(jié)束清理臟數(shù)據(jù)配合 fixture yield 后置import pytest import requests import pymysql pytest.fixture(scopefunction) def db_and_token(): function級別每個用例執(zhí)行yield后置清理臟數(shù)據(jù) # 前置建立連接、獲取token conn pymysql.connect(host127.0.0.1,userroot,password123456,databasetest_command_log,charsetutf8mb4) cur conn.cursor(cursorpymysql.cursors.DictCursor) #登錄拿token login_resp requests.post(http://xxx/api/login,json{username:zxj,password:123456}) token login_resp.json()[data][token] yield conn, cur, token # ?后置用例跑完刪除本次測試數(shù)據(jù)清理臟數(shù)據(jù) try: del_sql delete from command_log where commandName%s cur.execute(del_sql, (日志測試,)) conn.commit() except: conn.rollback() cur.close() conn.close() def test_add_log_case(db_and_token): conn,cur,token db_and_token headers {token:token} body {commandName:日志測試,commandValue:test001} resp requests.post(http://xxx/api/addLog,headersheaders,jsonbody) assert resp.json()[code] 0 #數(shù)據(jù)庫斷言 sql select commandName,commandValue from command_log where commandName%s order by create_time desc limit 1 cur.execute(sql,(日志測試,)) db_data cur.fetchone() assert db_data[commandValue] test001上面這個 fixture 是scopefunction每一條測試用例執(zhí)行完成都會執(zhí)行 delete 清理測試數(shù)據(jù)不會留臟數(shù)據(jù)。