支付平臺(tái)UI自動(dòng)化方案設(shè)計(jì)與實(shí)踐應(yīng)用

支付平臺(tái)UI自動(dòng)化方案設(shè)計(jì)與實(shí)踐應(yīng)用

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ---姜家豪?

?支付平臺(tái)是環(huán)球易購旗下各電商平臺(tái)用戶支付的服務(wù)系統(tǒng),擔(dān)任用戶購物下單支付環(huán)節(jié)中非常重要的角色。支付服務(wù)的穩(wěn)定性和質(zhì)量要求較高,因此測試人員在質(zhì)量控制和測試手段需要不斷提高和改進(jìn),引入U(xiǎn)I自動(dòng)化測試。


背景

起初,支付平臺(tái)只支持較少的支付方式,且僅接入電子(GB)一個(gè)站,演變到現(xiàn)在支持50多種支付方式,且接入GB,ZF,RG,RW,CB,D網(wǎng)等站點(diǎn)。過程的業(yè)務(wù)場景越來越復(fù)雜,特別是收銀臺(tái)信用卡類的表單校驗(yàn),驗(yàn)證點(diǎn)比較多且關(guān)聯(lián)所有網(wǎng)站,在每次發(fā)版前為保證質(zhì)量,回歸測試需要至少2人/天的時(shí)間,耗費(fèi)過多的人力資源,甚至?xí)袦y試覆蓋不全和測試漏測的場景,導(dǎo)致支付平臺(tái)在上生產(chǎn)時(shí),漏測的問題會(huì)相對(duì)的增加。

因此,想要解決人力時(shí)間和覆蓋更全,所以引入U(xiǎn)I自動(dòng)化來作回歸測試和校驗(yàn)表單功能。目的是為提高回歸測試效率與節(jié)省測試資源,覆蓋更多的業(yè)務(wù)測試場景,以保證每次上線的質(zhì)量。

一.設(shè)計(jì)

1、設(shè)計(jì)思路分析

1、核心功能點(diǎn)火,核心業(yè)務(wù)流

? ?1.常規(guī)下單支付流程;

? ?2.跳轉(zhuǎn)到第三方支付頁面兼容環(huán)境的不穩(wěn)定性。

2、支持重復(fù)操作的功能

? ?如表單校驗(yàn),重復(fù)提交等。

3、支持可擴(kuò)展性。

? ?綜上因素分析,因此排除使用公司自研的自動(dòng)化平臺(tái)和自動(dòng)化框架Robot Framework(ride),而使用更加靈活的Python+selenium+unittest 實(shí) 現(xiàn)UI自動(dòng)化。

2、功能設(shè)計(jì)

1.支持多端。PC,m,android,ios

2.代碼需健壯。加入多環(huán)境元素判斷

3.執(zhí)行時(shí)間要短。加入接口,數(shù)據(jù)庫與實(shí)現(xiàn)邏輯優(yōu)化

4.代碼編寫要便利。加入方法封裝

5.代碼后期維護(hù)成本要低。加入業(yè)務(wù)分離

6.代碼兼容性要強(qiáng)。加入元素分離和數(shù)據(jù)分離

7.支持自動(dòng)化需求測試。加入腳本測試需求

………


3.框架設(shè)計(jì)



1.介紹下整個(gè)框架目錄結(jié)構(gòu):



2.每個(gè)目錄與文件結(jié)構(gòu)的作用

1.UI_AUTOMATION目錄

Config:敏感數(shù)據(jù)分離存放地方,便于修改和管理。

Log:存放log,生成的日志。

driver:用于存放瀏覽器驅(qū)動(dòng)。

data: 該目錄用于存放測試相關(guān)的數(shù)據(jù)。

report:用于存放HTML測試報(bào)告。

utils: 用于存放自動(dòng)化測試擴(kuò)展包,例:config.py , HTMLTestRunner.py。

2.test目錄

API:存放接口,用接口執(zhí)行縮短UI執(zhí)行的時(shí)間,提高用例執(zhí)行效率。

page:分端文件集合,定位等selenium功能二次封裝

test_case: 測試用例目錄,存放用例及相關(guān)模塊。

3.testcase目錄

testsuie: 存放dubug調(diào)試,各端case集合在testcase.py

4.suie目錄

run_test: 項(xiàng)目主程序。用于運(yùn)行自動(dòng)化用例。

此設(shè)計(jì)的思路為更好的實(shí)現(xiàn)支付自動(dòng)化執(zhí)行和表單校驗(yàn)執(zhí)行,業(yè)務(wù),數(shù)據(jù),元素完全分離,層層遞進(jìn),減少維護(hù)成本。


二.unittest框架應(yīng)用實(shí)戰(zhàn)

unittest的屬性如下:

1.測試固件(test fixture)

TestCase類的屬性如下:

setUp():setUp()方法用于測試用例執(zhí)行前的初始化工作。如測試用例中需要訪問數(shù)據(jù)庫,可以在setUp中建立數(shù)據(jù)庫連接并進(jìn)行初始化。如測試用例需要登錄web,可以先實(shí)例化瀏覽器。

tearDown():tearDown()方法用于測試用例執(zhí)行之后的善后工作。如關(guān)閉數(shù)據(jù)庫連接。關(guān)閉瀏覽器。


assert*():一些斷言方法:在執(zhí)行測試用例的過程中,最終用例是否執(zhí)行通過,是通過判斷測試得到的實(shí)際結(jié)果和預(yù)期結(jié)果是否相等決定的。

2.測試套件1?(test suite 集合TestCase)

unittest.TestCase:TestCase類,所有測試用例類繼承的基本類。

BaseTestCase(unittest.TestCase):

unittest.main():使用這個(gè)方法可以方便的將一個(gè)單元測試模塊變?yōu)榭芍苯舆\(yùn)行的測試腳本,main()方法使用TestLoader類來搜索所有包含在該模塊中以“test”命名開頭的測試方法。


unittest.TestSuite():unittest框架的TestSuite()類是用來創(chuàng)建測試套件的。


3.測試套件2(test suite 獨(dú)立TestCase)

TestSuite類的屬性如下:(組織用例時(shí)需要用到)

將測試用例添加到測試套件中,如下方,是將test_GB模塊下的BaseTestCase類下的test_GB_paypal測試用例添加到測試套件。

suite = unittest.TestSuite()

suite.addTest(BaseTestCase(‘test_GB_paypal’))


4.測試執(zhí)行器?(test runner)

TextTextRunner的屬性如下:(組織用例時(shí)需要用到)

run(): run()方法是運(yùn)行測試套件的測試用例,入?yún)閟uite測試套件。

runner = unittest.TextTestRunner()

runner.run(suite)



三.實(shí)現(xiàn)用戶下單支付的自動(dòng)化case

【creditcard支付】

1.編寫case

登陸:

def gb_login(self):

? ? pc = HomePage(self.driver)

? ? lg = LoginPage(self.driver)

? ? pc.get(self.GBloginURL)

? ? lg.login(self.username, self.password)

? ? sleep(1)

商品購買

def gb_goodtobuy(self):

? ? sleep(0.5)

? ? try:

? ? ? ? self.driver.get(self.GBgood)

? ? ? ? b = 0

? ? ? ? while b != 5:

? ? ? ? ? ? try:

? ? ? ? ? ? ? ? self.find_element(*GoodsInfoPageLoc.buy_button)

? ? ? ? ? ? ? ? b = 5

? ? ? ? ? ? except:

? ? ? ? ? ? ? ? b = b + 1

? ? ? ? ? ? ? ? self.driver.refresh()

? ? except TimeoutException:

? ? ? ? self.driver.execute_script('window.stop()')

? ? self.find_element(*GoodsInfoPageLoc.buy_button)

? ? try:

? ? ? ? self.click(*GoodsInfoPageLoc.num_plus)

? ? ? ? self.click(*GoodsInfoPageLoc.buy_button)

? ? except:

? ? ? ? self.driver.refresh()

? ? ? ? self.click(*GoodsInfoPageLoc.num_plus)

? ? ? ? self.click(*GoodsInfoPageLoc.buy_button)

? ? sleep(5)

訂單確認(rèn):

def gb_placeorder(self):

? ? sleep(1)

? ? try:

? ? ? ? self.driver.switch_to.alert

? ? ? ? self.driver.switch_to.alert.accept()? # 點(diǎn)擊彈出上面的X按鈕

? ? except:

? ? ? ? pass

? ? try:

? ? ? ? self.click(*PlaceInfoPageLoc.place_button)? # pleace your order

? ? except:

? ? ? ? self.driver.refresh()

? ? ? ? sleep(1)

? ? ? ? self.driver.execute_script("var q=document.documentElement.scrollTop=500")

? ? ? ? try:

? ? ? ? ? ? self.click(*PlaceInfoPageLoc.place_button)? # pleace your order

? ? ? ? except:

? ? ? ? ? ? self.driver.refresh()

? ? ? ? ? ? sleep(1)

? ? ? ? ? ? self.click(*PlaceInfoPageLoc.place_button)? # pleace your order

? ? sleep(4)


收銀臺(tái)支付:

def payment_creditcard(self):

? ? self.find_element(*PaymentInfoPageLoc.pay_button)

? ? self.driver.refresh()

? ? sleep(0.5)

? ? print('收銀臺(tái)url:', self.driver.current_url)

? ? self.obs_credit_channel("checkout_credit")

? ? self.click(*PaymentInfoPageLoc.creditcard_src)

? ? self.send_keys("hao", *PaymentInfoPageLoc.creditcard_holder)

? ? self.send_keys("6011511651111117", *PaymentInfoPageLoc.creditcard_Number)

? ? self.send_keys("155", *PaymentInfoPageLoc.creditcard_Code)

? ? self.send_Skeys("4", *PaymentInfoPageLoc.creditcard_mouth)

? ? self.send_Skeys("2035", *PaymentInfoPageLoc.creditcard_day)

? ? sleep(0.8)

? ? self.click(*PaymentInfoPageLoc.pay_button)

? ? sleep(4)


支付結(jié)果和斷言:

def gb_checkorder(self):

? ? try:

? ? ? ? paymentresult = self.driver.find_element_by_xpath("http://*[@class='pay_title']").text

? ? ? ? print("支付結(jié)果提示語:", paymentresult)

? ? ? ? global ordersn

? ? ? ? try:

? ? ? ? ? ? ordersn = self.driver.find_element_by_xpath("http://*[@class='payOnline_sucess']/p[2]/b[1]").text? ? ? ? ? ? ? ? ? ? ? except:

? ? ? ? ? ? ? ? ? ? ? ? print("fail or no_result")

? ? ? ? ordersn = str(ordersn)

? ? ? ? if len(ordersn) == 20:

? ? ? ? ? ? pass

? ? ? ? else:

? ? ? ? ? ? ordersn = ordersn[:-1]

? ? ? ? print("訂單號(hào)為:", ordersn)

? ? ? ? connect = pymysql.connect(host=self.host, port=self.post, user=self.user, passwd=self.passwd, db=self.db,charset='utf8')

? ? ? ? cursor = connect.cursor()

? ? ? ? cursor.execute("SELECT pay_status FROM pay_gateway_16 WHERE parent_order_sn=('%s')" % (ordersn))

? ? ? ? status = cursor.fetchall()

? ? ? ? status = str(status)

? ? ? ? status = status[2:3]

? ? ? ? print("此訂單狀態(tài)為:【%s】" % (status), " (0-未支付 1-處理中 2-已支付 3-退款中 4-退款成功 5退款失敗 6支付失敗)")

? ? ? ? cursor.close()

? ? except Exception as msg:

? ? ? ? print(u"異常原因%s" % msg)

? ? ? ? self.save_screen_shot()

? ? ? ? raise


2.執(zhí)行case

# # 構(gòu)造測試集

suite = unittest.TestSuite()

suite.addTest(BaseTestCase('login_GB'))

suite.addTest(BaseTestCase('test_GB_creditcard'))

now_time = time.strftime('%Y%m%d-%H%M%S', time.localtime())

report = REPORT_PATH + '\\' + 'PAY_Report ' + now_time + '.html'

### 從代碼執(zhí)行結(jié)果里面獲取數(shù)據(jù)? ? ? HTMLTestRunner

with open(report, "wb") as outfile:

? ? # runner = HTMLTestRunner(stream=outfile, title=u"GB-PC-PAY_UITest", description=u"用例執(zhí)行情況:")

? ? runner = HTMLTestRunner(stream=outfile, title=u"GB-PC-PAY_UITest", description=u"用例執(zhí)行情況:", verbosity=2, retry=0, save_last_try=True)

? ? aa = runner.run(suite)

e = Email(path=report, message='''

本郵件是支付UI自動(dòng)化測試報(bào)告,請(qǐng)下載或者使用瀏覽器查看附件內(nèi)容.

如果有任何問題,請(qǐng)及時(shí)聯(lián)系:soa支付測試組. Thank you!

''')

e.send()

3.測試結(jié)果

最后自動(dòng)發(fā)送執(zhí)行結(jié)果郵件報(bào)告:



四.前端表單校驗(yàn)的設(shè)計(jì)和功能的實(shí)現(xiàn)

方法封裝:

將常用的方法封裝起來,包含等待,對(duì)比,輸出對(duì)比結(jié)果。

def is_clickable(self, xpath, timeout=5):

? ? try:

? ? ? ? ui.WebDriverWait(self.driver, timeout).until(EC.element_to_be_clickable((By.XPATH, xpath)))

? ? ? ? return True

? ? except TimeoutException:

? ? ? ? return False

def is_text_equal(self, xpath, text, Description, timeout=3):

? ? sleep(0.3)

? ? try:

? ? ? ? ui.WebDriverWait(self.driver, timeout).until(EC.element_to_be_clickable((By.XPATH, xpath)))

? ? ? ? locator = ("xpath", xpath)

? ? ? ? text = text

? ? ? ? a = EC.text_to_be_present_in_element(locator, text)(self.driver)

? ? ? ? if a:

? ? ? ? ? ? print(a, "【%s】:校驗(yàn)通過"%Description)

? ? ? ? else:

? ? ? ? ? ? print(a, "【%s】:校驗(yàn)失敗請(qǐng)檢查"%Description)

? ? ? ? ? ? self.driver.get_screenshot_as_base64()

? ? except TimeoutException:

? ? ? ? self.driver.get_screenshot_as_base64()

? ? ? ? print("超時(shí),請(qǐng)檢查環(huán)境是否異常")

? ? sleep(0.2)

def form_input(self, xpath, send_keys):? ? ? #第一個(gè)參數(shù)xpath:輸入的位置,第二個(gè)參數(shù)send_key:輸入的內(nèi)容

? ? self.driver.find_element_by_xpath(xpath).clear()

? ? self.driver.find_element_by_xpath(xpath).send_keys(send_keys)

? ? sleep(0.2)

? ? self.driver.find_element_by_xpath(xpath_activation).click()? # 激活校驗(yàn)

? ? sleep(0.3)

def isElementExist(self, xpath, Description):

? ? sleep(0.3)

? ? a = self.driver.find_element_by_xpath(xpath).text

? ? if a == '':

? ? ? ? print("True", "【%s】:校驗(yàn)通過" % Description)

? ? else:

? ? ? ? print("False", "【%s】:校驗(yàn)失敗請(qǐng)檢查" % Description)

? ? sleep(0.2)


表單校驗(yàn):



表單校驗(yàn)代碼實(shí)現(xiàn):

思想:使用UI自動(dòng)化自動(dòng)執(zhí)行文本的輸入和激活校驗(yàn),對(duì)比激活校驗(yàn)提示語。

這樣實(shí)現(xiàn)雙重校驗(yàn),既檢查校驗(yàn)的表單的功能,也檢查校驗(yàn)提示語的正確性。

def test_formtest_creditcard(self):

? ? self.is_clickable("http://*[@class='placeOrder btn block toPayBtn']")

? ? print(self.driver.current_url)

? ? sleep(0.5)

self.driver.find_element_by_xpath('//*[@src="https://uidesign.zafcdn.com/ZF/image/z_promo/20190418_9281/discover.png"]').click()

? ? sleep(1)

? ? print("------------------------------------------------------------------")

? ? print("billing address元素個(gè)數(shù)校驗(yàn):")

? ? self.driver.find_element_by_xpath('//*[@class="credit_editAddress"]').click()

? ? sleep(1)

? ? num = len(self.driver.find_elements_by_xpath("http://*[@class='credit_editForm']/div"))

? ? n = 10

? ? if num == 10:

? ? ? ? print("billing address元素%d個(gè)校驗(yàn)通過" %n)

? ? else:

? ? ? ? print("billing address元素%d個(gè)校驗(yàn)不通過,請(qǐng)檢查?。。。。。。。。。?!" %n)

? ? print("------------------------------------------------------------------")

? ? try:

? ? ? ? sleep(0.5)

? ? ? self.driver.find_element_by_xpath('//*[@src="https://uidesign.gbtcdn.com/GB/images/others/check_out/57x35/discover.png?imbypass=true"]').click()

? ? ? ? sleep(0.5)

? ? ? ? print("Card numbers校驗(yàn):")

? ? ? ? self.driver.find_element_by_xpath("http://*[@class='placeOrder btn block toPayBtn']").click()? # 激活校驗(yàn)

? ? ? ? sleep(0.5)

? ? ? ? self.is_text_equal('//*[@curchannel="CREDITCARD"]/div[1]/div/p', 'Please enter your card number.', 'Card numbers為空校驗(yàn)')

? ? ? ? self.form_input("http://*[@placeholder='Card Number']", "1234567890")

? ? ? ? self.is_text_equal('//*[@curchannel="CREDITCARD"]/div[1]/div/p', 'Card numbers must contain between 12 and 20 numerical characters.' ,'Card numbers長度校驗(yàn)')

? ? ? ? self.form_input("http://*[@placeholder='Card Number']", "123456789012")

? ? ? ? self.is_text_equal('//*[@curchannel="CREDITCARD"]/div[1]/div/p',

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 'Invalid card numbers. Please kindly make sure that your card numbers are correct.',

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 'Card numbers無效卡校驗(yàn)')

? ? ? ? self.form_input("http://*[@placeholder='Card Number']", "6011111111111117")

? ? ? ? self.isElementExist('//*[@curchannel="CREDITCARD"]/div[1]/div/p', 'Card numbers有效卡校驗(yàn)')

? ? ? ? print("------------------------------------------------------------------")

except Exception as msg:

? ? print(u"異常原因%s" % msg)

? ? self.save_screen_shot()

? ? raise


執(zhí)行的結(jié)果:




五.業(yè)務(wù)需求測試代碼實(shí)現(xiàn)

舉例子:有如下業(yè)務(wù)需求

代碼實(shí)現(xiàn):

driver = webdriver.Chrome()

driver.get(GBConnect().ZF_payurl())

driver.maximize_window()

driver.refresh()

sleep(0.5)

class CPF():

? ? def baxicpf(cpf):

? ? ? ? #巴西分期:

? ? ? ? src = '//*[@src="https://uidesign.gbtcdn.com/GB/images/others/check_out/57x35/Hipercard.png?imbypass=true"]'

? ? ? ? CPF = "http://*[@placeholder='CPF']"

? ? ? ? CPFerror = CPF + "/../../p"

? ? ? ? sleep(0.5)

? ? ? ? driver.find_element_by_xpath(src).click()

? ? ? ? while 1:

? ? ? ? ? ? try:

? ? ? ? ? ? ? ? driver.find_element_by_xpath(src).click()

? ? ? ? ? ? ? ? break

? ? ? ? ? ? except:

? ? ? ? ? ? ? ? driver.execute_script("var q=document.body.scrollTop=300")

? ? ? ? ? ? ? ? sleep(0.2)

? ? ? ? ? ? ? ? driver.find_element_by_xpath(src).click()

? ? ? ? sleep(0.2)

? ? ? ? driver.find_element_by_xpath(CPF).clear()

? ? ? ? driver.find_element_by_xpath(CPF).clear()

? ? ? ? driver.find_element_by_xpath(CPF).send_keys(cpf)

? ? ? ? driver.find_element_by_xpath("http://*[@class='placeOrder btn block toPayBtn']").click()

? ? ? ? sleep(0.2)

? ? ? ? c = driver.find_element_by_xpath(CPFerror).text

? ? ? ? print("巴西分期CPF輸入[%s] =======>> 校驗(yàn)結(jié)果: %s" % (cpf, c))

? ? def boletocpf(cpf):

? ? ? ? #boleto:

? ? ? ? src = '//*[@src="https://icss1.gearbest.com/imagecache/GB2/images/boleto3.jpg"]'

? ? ? ? CPF = "http://*[@placeholder='CPF']"

? ? ? ? CPFerror = CPF + "/../../p"

? ? ? ? sleep(0.5)

? ? ? ? driver.find_element_by_xpath(src).click()

? ? ? ? while 1:

? ? ? ? ? ? try:

? ? ? ? ? ? ? ? driver.find_element_by_xpath(src).click()

? ? ? ? ? ? ? ? break

? ? ? ? ? ? except:

? ? ? ? ? ? ? ? driver.execute_script("var q=document.body.scrollTop=300")

? ? ? ? ? ? ? ? sleep(0.2)

? ? ? ? ? ? ? ? driver.find_element_by_xpath(src).click()

? ? ? ? sleep(0.2)

? ? ? ? driver.find_element_by_xpath(CPF).clear()

? ? ? ? driver.find_element_by_xpath(CPF).send_keys(cpf)

? ? ? ? driver.find_element_by_xpath("http://*[@class='placeOrder btn block toPayBtn']").click()

? ? ? ? sleep(0.2)

? ? ? ? c = driver.find_element_by_xpath(CPFerror).text

? ? ? ? print("boileto CPF輸入[%s] =======>> 校驗(yàn)結(jié)果: %s" % (cpf, c))

def run():

? ? i = 0

? ? while 1:

? ? ? ? cpf = "".join(random.choice("0123456789") for i in range(11))

? ? ? ? # cpf = '38518065305'

? ? ? ? print(type(cpf), cpf)

? ? ? ? j = (int(cpf[0])*10 + 9*int(cpf[1]) + 8*int(cpf[2]) + 7*int(cpf[3]) + 6*int(cpf[4]) + 5*int(cpf[5]) + 4*int(cpf[6]) + 3*int(cpf[7]) + 2*int(cpf[8]))%11

? ? ? ? print(j)

? ? ? ? if j == 0 or j == 1:

? ? ? ? ? ? j = 0

? ? ? ? else:

? ? ? ? ? ? j = 11 - j

? ? ? ? print("最終", j)

? ? ? ? newcpf = list(cpf)

? ? ? ? newcpf[9] = str(j)

? ? ? ? cpf = "".join(newcpf)

? ? ? ? k = (int(cpf[0])*11 + 10*int(cpf[1]) + 9*int(cpf[2]) + 8*int(cpf[3]) + 7*int(cpf[4]) + 6*int(cpf[5]) + 5*int(cpf[6]) + 4*int(cpf[7]) + 3*int(cpf[8]) + 2*int(cpf[9]))%11

? ? ? ? print(k)

? ? ? ? if k == 0 or k == 1:

? ? ? ? ? ? k = 0

? ? ? ? else:

? ? ? ? ? ? k = 11 - k

? ? ? ? print("最終", k)

? ? ? ? newcpf = list(cpf)

? ? ? ? newcpf[10] = str(k)

? ? ? ? newcpf = ''.join(newcpf)

? ? ? ? print("上面CPF合法應(yīng)是:", newcpf)

? ? ? ? print("---------------------------------")

? ? ? ? if cpf[9] == str(j) and cpf[10] == str(k):

? ? ? ? ? ? print("合法的CPF:", cpf)

? ? ? ? ? ? print("嘗試[%i]次后成功" %i)

? ? ? ? ? ? strs = ['baxicpf','boletocpf']

? ? ? ? ? ? CPF.boletocpf(cpf)

? ? ? ? ? ? break

? ? ? ? else:

? ? ? ? ? ? strs = ['baxicpf', 'boletocpf']

? ? ? ? ? ? # for s in strs:

? ? ? ? ? ? CPF.boletocpf(cpf)

? ? ? ? ? ? i += 1

run()


收到信用卡表單校驗(yàn)測試:


執(zhí)行輸出結(jié)果:

<class 'str'> 53215938442

10

最終 1

8

最終 3

上面CPF合法應(yīng)是: 53215938413

---------------------------------

boileto CPF輸入[98100879934] =======>> 校驗(yàn)結(jié)果: Entre um válido CPF

<class 'str'> 71939488764

8

最終 3

4

最終 7

上面CPF合法應(yīng)是: 71939488737

---------------------------------

......

......

---------------------------------

boileto CPF輸入[71939488734] =======>> 校驗(yàn)結(jié)果: Entre um válido CPF

<class 'str'> 62332668488

1

最終 0

8

最終 3

上面CPF合法應(yīng)是: 62332668403

---------------------------------

合法的CPF: 96591066020

嘗試[10]次后成功

boileto CPF輸入[96591066020] =======>> 校驗(yàn)結(jié)果: 校驗(yàn)通過

業(yè)務(wù)需求規(guī)則,用代碼編寫測試規(guī)則,測試前端實(shí)現(xiàn)邏輯,實(shí)現(xiàn)需求的全覆蓋。

?

以上是我對(duì)支付平臺(tái)UI自動(dòng)化設(shè)計(jì)思想,實(shí)現(xiàn)框架,實(shí)踐方案的簡單介紹,謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容