You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 19:07:37