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

Python井字棋自动化测试:Selenium超时与递归逻辑优化求助

井字棋UI测试机器人问题解决思路

核心问题分析

  1. 胜负元素检查超时:WebDriverWait().until(EC.presence_of_element_located)会强制等待元素出现,游戏未结束时胜负提示元素不存在,直接触发超时异常。
  2. 递归死循环:原代码中递归调用test_playTTT,每次调用都会重新初始化变量,导致游戏状态丢失且无法正常终止。

解决方案

1. 安全检查胜负元素(避免超时)

不要用强制等待元素出现的方法,改用尝试查找元素并捕获异常,或用find_elements(复数形式)判断元素是否存在(找不到时返回空列表,不会抛出异常):

def checkForWinner(self, load_browser):
    # 用find_elements判断元素是否存在,避免超时异常
    winnerOh = load_browser.find_elements(By.XPATH, Tags.resultOh)
    winnerEx = load_browser.find_elements(By.XPATH, Tags.resultEx)
    tiedGame = load_browser.find_elements(By.XPATH, Tags.resultTie)
    
    if winnerOh:
        LOGGER.info('Winner O')
        return 'O'
    elif winnerEx:
        LOGGER.info('Winner X')
        return 'X'
    elif tiedGame:
        LOGGER.info('Tie')
        return 'None'
    else:
        # 游戏未结束,返回空字符串
        return ''

如果需要等待AI落子后的短暂渲染时间,可添加带异常忽略的短时间等待:

from selenium.common.exceptions import TimeoutException

def checkForWinner(self, load_browser):
    try:
        winnerOh = WebDriverWait(load_browser, 2, ignored_exceptions=[TimeoutException]).until(EC.presence_of_element_located((By.XPATH, Tags.resultOh)))
        LOGGER.info('Winner O')
        return 'O'
    except TimeoutException:
        pass
    
    try:
        winnerEx = WebDriverWait(load_browser, 2, ignored_exceptions=[TimeoutException]).until(EC.presence_of_element_located((By.XPATH, Tags.resultEx)))
        LOGGER.info('Winner X')
        return 'X'
    except TimeoutException:
        pass
    
    try:
        tiedGame = WebDriverWait(load_browser, 2, ignored_exceptions=[TimeoutException]).until(EC.presence_of_element_located((By.XPATH, Tags.resultTie)))
        LOGGER.info('Tie')
        return 'None'
    except TimeoutException:
        pass
    
    return ''

2. 替换递归为迭代循环(避免死循环)

原递归逻辑会丢失游戏状态,改用循环迭代持续进行游戏,直到分出胜负或平局:

def test_playTTT(self, load_browser):
    squares = [Tags.square1,Tags.square2,Tags.square3,
               Tags.square4,Tags.square5,Tags.square6,
               Tags.square7,Tags.square8,Tags.square9]
    
    clickedSquares = []
    winner = ''
    
    # 循环直到游戏结束
    while not winner:
        # 检查当前胜负状态
        winner = self.checkForWinner(load_browser)
        if winner:
            break
        
        # 筛选未被点击的格子索引
        available_squares = [i for i in range(9) if i not in clickedSquares]
        if not available_squares:
            # 无可用格子,判定平局
            winner = 'None'
            break
        
        # 随机选择可用格子并点击
        random_square = choice(available_squares)
        element = load_browser.find_element(By.XPATH, squares[random_square])
        element.click()
        clickedSquares.append(random_square)
        
        # 等待AI落子(适配React渲染延迟)
        time.sleep(1)
        
        # 收集AI落子的格子(修正原逻辑错误:检查格子实际内容而非XPATH字符串)
        for i in range(9):
            square_element = load_browser.find_element(By.XPATH, squares[i])
            # 假设O格子的文本为'O',可根据实际UI调整判断条件
            if square_element.text == 'O' and i not in clickedSquares:
                clickedSquares.append(i)
    
    LOGGER.info(f"Game ended. Winner: {winner}")

3. 原代码其他问题修正

  • 原代码中if squares[i] == Tags.ohSquare逻辑错误:squares[i]是XPATH字符串,Tags.ohSquare应为O格子的实际标识(如文本、class),需改为检查元素的实际属性或内容。
  • 移除不必要的递归调用,避免重复初始化变量导致的状态丢失。

内容的提问来源于stack exchange,提问作者CodenameCaptainFuture

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 21:20:44