我一直在思考第一個博客應該寫什么,然后我就解決了開通博客后解決的第一個問題,擇題不如撞題;
如果大多數人和我一樣,接觸python+selenium+unittest是從selenium IDE開始的話,你也一定會遇到這樣的問題:
我們寫了5個,10個,甚至20個測試用例,放在一個py腳本里,每個測試用例執行完畢之后,都會走一遍退出瀏覽器的操作,然后再啟動瀏覽器,再退出,如此反復,浪費了大量的時間,今天就說說我研究解決這個問題的歷程;
我們第一個錄制並導出的python selenium代碼,萬能的“登錄功能”,是這個樣子的:
# -*- 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.ui import Select from selenium.common.exceptions import NoSuchElementException from selenium.common.exceptions import NoAlertPresentException import unittest, time, re class Login(unittest.TestCase): def setUp(self): self.driver = webdriver.Firefox() self.driver.implicitly_wait(30) self.base_url = "http://192.168.1.207:8080" self.verificationErrors = [] self.accept_next_alert = True def test_login(self): driver = self.driver driver.get(self.base_url + "/QAS/RchWebSchool/") driver.find_element_by_css_selector("span").click() driver.find_element_by_name("j_password").clear() driver.find_element_by_name("j_password").send_keys("admin") driver.find_element_by_name("j_username").clear() driver.find_element_by_name("j_username").send_keys("admin") driver.find_element_by_xpath("//button[@type='submit']").click() def is_element_present(self, how, what): try: self.driver.find_element(by=how, value=what) except NoSuchElementException as e: return False return True def is_alert_present(self): try: self.driver.switch_to_alert() except NoAlertPresentException as e: return False return True def close_alert_and_get_its_text(self): try: alert = self.driver.switch_to_alert() alert_text = alert.text if self.accept_next_alert: alert.accept() else: alert.dismiss() return alert_text finally: self.accept_next_alert = True def tearDown(self): self.driver.quit() self.assertEqual([], self.verificationErrors) if __name__ == "__main__": unittest.main()
往往我們很容易讀懂這三個def(如果讀不懂,請看蟲師先生的博客:selenium-webdriver(python) (十六) --unittest 框架):
setUp(self)
test_login(self)
tearDown(self)
讀懂之后,我們就可以寫另外一個功能“修改個人信息”的自動化腳本了,假設我們在class:Login 里是這樣寫的:
def setUp(self): self.driver = webdriver.Firefox() self.driver.implicitly_wait(30) self.base_url = "http://192.168.1.207:8080" self.verificationErrors = [] self.accept_next_alert = True def test_login(self): driver = self.driver driver.get(self.base_url + "/QAS/RchWebSchool/") driver.find_element_by_css_selector("span").click() driver.find_element_by_name("j_password").clear() driver.find_element_by_name("j_password").send_keys("admin") driver.find_element_by_name("j_username").clear() driver.find_element_by_name("j_username").send_keys("admin") driver.find_element_by_xpath("//button[@type='submit']").click() def test_login_reg(self): driver = self.driver driver.get(self.base_url + "/QAS/RchWebSchool/") #登錄代碼 ... #修改個人信息代碼 ... def tearDown(self): self.driver.quit() self.assertEqual([], self.verificationErrors)
我們發現,每增加一個測試用例,都增加一次操作:啟動瀏覽器(setUp)→登錄測試網址(#登錄代碼)→關閉瀏覽器(teardown),而當用例過多的時候,如此反復的操作會浪費大量的時間;
怎么辦呢?
如果我們能只走一次啟動瀏覽器和退出瀏覽器,那該有多好啊;
我們嘗試將所有的功能的操作都寫在一個test里面,我們的test_login(self)代碼就變成了這樣:
def test_login(self): driver = self.driver driver.get(self.base_url + "/QAS/RchWebSchool/") driver.find_element_by_css_selector("span").click() driver.find_element_by_name("j_password").clear() driver.find_element_by_name("j_password").send_keys("admin") driver.find_element_by_name("j_username").clear() driver.find_element_by_name("j_username").send_keys("admin") driver.find_element_by_xpath("//button[@type='submit']").click() #修改個人信息 ... #功能續3 ... #功能續4 ...
但是,這樣就了一個新的問題,當我們只測試其中一個功能,或者用到其中某一個功能的時候,沒有辦法單獨調用某一個功能的代碼,失去了原本的靈活性;
所以陽陽針對class:Login里的代碼進行這樣改(請看注釋):
1 dr = webdriver.Firefox()#定義全局的dr,不在setUp函數里,實現了只啟動一次firefox 2 def setUp(self,driver= dr): 3 self.driver = driver#初始化數據中,將全局變量dr賦值給self.driver,這樣每次測試開始都只會鎖定dr,而不是新創建一個 4 self.driver.implicitly_wait(30) 5 self.base_url = "http://192.168.1.207:8081" 6 self.verificationErrors = [] 7 self.accept_next_alert = True 8 9 def test_login(self): 10 driver = self.driver 11 driver.get(self.base_url + "/RchWebSchool/") 12 driver.find_element_by_css_selector("span").click() 13 driver.find_element_by_name("j_password").clear() 14 driver.find_element_by_name("j_password").send_keys("admin") 15 driver.find_element_by_name("j_username").clear() 16 driver.find_element_by_name("j_username").send_keys("admin") 17 driver.find_element_by_xpath("//button[@type='submit']").click() 18 19 def test_login_reg(self): 20 driver = self.driver 21 #driver.get(self.base_url + "/RchWebSchool/") 22 ... 23 #修改個人信息自動化代碼 24 ... 25 26 def test_zzz_quit(self):#最后一個測試用例,實現了只進行一次退出瀏覽器 27 self.driver.quit() 28 29 30 def tearDown(self): 31 try: 32 self.driver.refresh()#將退出瀏覽器的操作變成刷新瀏覽器,用於不同用例之間的接洽操作 33 except ConnectionRefusedError as e: 34 print(e) 35 finally: 36 self.assertEqual([], self.verificationErrors)
大家應該看到,陽陽把tearown(self)里面的代碼已經改的面目全非了,這是因為我們每個測試用例(test_xxx)執行之后,都會走一次tearown,最后一個退出瀏覽器的test_zzz_quit當然也不例外,所以在走完test_zzz_quit之后,一定會再次執行刷新瀏覽器的操作,但是此前瀏覽器已經被關閉了,刷新瀏覽器一定會報錯,所以把異常ConnectionRefusedError拋出,不再進行處理,視為測試用例通過;
當然,如果自動化只到這里的話,大家也可把teardown干掉,把每次刷新瀏覽器的操作放在每個測試用例的最后面,都是可以的,我只是習慣性保持unittest的完整性;