Python Selenium Unittest多URL测试问题:循环总是获取最后一个URL的求解
问题根源分析
你遇到的问题是因为在类定义阶段直接用for循环重复定义测试方法导致的。Python在处理类定义时会按顺序执行代码,每次循环都会重新覆盖setUp、test_load_home_page等方法的定义。而且因为闭包的延迟绑定特性,当测试方法真正执行时,url变量已经指向了循环的最后一个元素,所以所有测试都会用最后一个URL。
另外,unittest框架是通过方法名来识别测试用例的,你这样重复定义方法,最终只会保留最后一次循环生成的版本,自然只会测试最后一个URL。
解决方案一:使用unittest的subTest上下文管理器(推荐)
这是unittest官方推荐的处理多参数测试的方式,简单直接,能让每个URL作为独立的测试子用例执行,且不会互相影响。修改后的代码如下:
import re import unittest from selenium import webdriver from selenium.webdriver.support import expected_conditions as ec from selenium.webdriver.support.ui import WebDriverWait # credentials (Hardcoded) userName = 'user' passWord = 'password' class TestingLoginPage(unittest.TestCase): test_urls = ['https://url1.com', 'https://url2.com'] def setUp(self): self.driver = webdriver.Chrome('chromedriver') self.driver.implicitly_wait(5) # --- Steps --- def test_load_home_page(self): driver = self.driver for url in self.test_urls: # 用subTest标记每个子测试,失败时能明确知道是哪个URL出问题 with self.subTest(url=url): driver.get(url) try: WebDriverWait(driver, 5).until(ec.title_is('Sign in')) print(f'Page {url} is loaded within 5 seconds.') except: print(f'Page {url} loading issue.') # 用self.fail标记测试失败,让unittest正确统计结果 self.fail(f'Failed to load page: {url}') def test_successful_login(self): driver = self.driver for url in self.test_urls: with self.subTest(url=url): driver.get(url) driver.find_element_by_id('j_username').send_keys(userName) driver.find_element_by_name('j_password').send_keys(passWord) driver.find_element_by_name('Submit').click() try: WebDriverWait(driver, 5).until(ec.title_is('Dashboard')) print(f'User logged in successfully to {url}') except: print(f'User login failed for {url}') self.fail(f'Login failed for: {url}') def test_failed_login(self): driver = self.driver for url in self.test_urls: with self.subTest(url=url): driver.get(url) # 注意:原代码这里用的是'username',而成功登录用的是'j_username',可能是笔误,需确认实际页面元素 driver.find_element_by_id('username').send_keys(userName) driver.find_element_by_name('password').send_keys('test') driver.find_element_by_name('Submit').click() try: src = driver.page_source text_found = re.search(r'wrong username or password', src) # 用更规范的assertIsNotNone断言 self.assertIsNotNone(text_found, f'Error message not found for {url}') print(f'Wrong credentials error correctly shown for {url}.') except AssertionError: print(f'User logged in with wrong credentials on {url}, check setup.') raise def tearDown(self): # 用quit()代替close(),彻底关闭浏览器进程,避免残留实例 self.driver.quit() if __name__ == '__main__': unittest.main()
解决方案二:动态生成测试方法
如果希望每个URL对应独立的测试方法名(方便在测试报告中区分),可以用动态生成方法的方式:
import re import unittest from selenium import webdriver from selenium.webdriver.support import expected_conditions as ec from selenium.webdriver.support.ui import WebDriverWait # credentials (Hardcoded) userName = 'user' passWord = 'password' class TestingLoginPage(unittest.TestCase): test_urls = ['https://url1.com', 'https://url2.com'] def setUp(self): self.driver = webdriver.Chrome('chromedriver') self.driver.implicitly_wait(5) def tearDown(self): self.driver.quit() # 定义测试方法工厂函数 def create_load_test(url): def test(self): driver = self.driver driver.get(url) try: WebDriverWait(driver, 5).until(ec.title_is('Sign in')) print(f'Page {url} is loaded within 5 seconds.') except: print(f'Page {url} loading issue.') self.fail(f'Failed to load page: {url}') return test def create_success_login_test(url): def test(self): driver = self.driver driver.get(url) driver.find_element_by_id('j_username').send_keys(userName) driver.find_element_by_name('j_password').send_keys(passWord) driver.find_element_by_name('Submit').click() try: WebDriverWait(driver, 5).until(ec.title_is('Dashboard')) print(f'User logged in successfully to {url}') except: print(f'User login failed for {url}') self.fail(f'Login failed for: {url}') return test def create_fail_login_test(url): def test(self): driver = self.driver driver.get(url) driver.find_element_by_id('username').send_keys(userName) driver.find_element_by_name('password').send_keys('test') driver.find_element_by_name('Submit').click() try: src = driver.page_source text_found = re.search(r'wrong username or password', src) self.assertIsNotNone(text_found, f'Error message not found for {url}') print(f'Wrong credentials error correctly shown for {url}.') except AssertionError: print(f'User logged in with wrong credentials on {url}, check setup.') raise return test # 为每个URL动态添加测试方法到类中 for idx, url in enumerate(TestingLoginPage.test_urls): # 生成唯一的方法名 load_test_name = f'test_load_home_page_{idx+1}_{url.replace("https://", "").replace(".com", "")}' setattr(TestingLoginPage, load_test_name, create_load_test(url)) success_test_name = f'test_successful_login_{idx+1}_{url.replace("https://", "").replace(".com", "")}' setattr(TestingLoginPage, success_test_name, create_success_login_test(url)) fail_test_name = f'test_failed_login_{idx+1}_{url.replace("https://", "").replace(".com", "")}' setattr(TestingLoginPage, fail_test_name, create_fail_login_test(url)) if __name__ == '__main__': unittest.main()
额外建议
- 用
self.fail()标记测试失败,这样unittest会正确统计测试通过率,而不是只打印信息。 - 断言尽量用unittest提供的专用方法(比如
assertIsNotNone),比直接比较None更规范。 tearDown中用driver.quit()代替close(),避免浏览器进程残留。- 注意
test_failed_login里的元素ID是username,和成功登录的j_username不一致,可能是笔误,需要确认实际页面的元素ID。
内容的提问来源于stack exchange,提问作者parames
相关产品推荐
相关产品推荐

