web自动化切换窗口的问题

问题

业务流程描述:
登录业务系统-点击应用-点击销售管理-点击商机报送-编辑商机信息并点击报送,在报送的页面获取商机名称和编号信息并用变量接收-再点击应用回到应用页面-点击工作流服务(此时点击完成后会打开一个新的窗口,然后需要再新的窗口中点击流程实例按钮,在这一步就会报错(我在点击前后都获取了所有窗口句柄信息,返回的句柄列表就只有一个相同的句柄,按这种我理解就还是在同一个窗口下,然后我也加了显示等待和强制等待,分别会提示超时和元素不可交互的报错提示)

报错信息

FAILED                         [100%]报送成功后的商机名称和商机编号分别是:autotestdata011,SJ-24-08-21-E53E82C7
['DF74ABB6CBF5334BAA09740E895D88F7']
['DF74ABB6CBF5334BAA09740E895D88F7']

test_sjbs_uat_env.py:19 (TestSjbs.test_sjbs)
self = <Web.Szc_Ltc_System.test_sjbs_uat_env.TestSjbs object at 0x0000019AC9BDAD30>

    
test_sjbs_uat_env.py:179: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\venv_hjh\lib\site-packages\selenium\webdriver\remote\webelement.py:94: in click
    self._execute(Command.CLICK_ELEMENT)
..\venv_hjh\lib\site-packages\selenium\webdriver\remote\webelement.py:395: in _execute
    return self._parent.execute(command, params)
..\venv_hjh\lib\site-packages\selenium\webdriver\remote\webdriver.py:354: in execute
    self.error_handler.check_response(response)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x0000019AC9EDE130>
response = {'status': 400, 'value': '{"value":{"error":"element not interactable","message":"element not interactable\\n  (Sessio...07FF7BD6EE16F]\\n\\tBaseThreadInitThunk [0x00007FFAD25B7374+20]\\n\\tRtlUserThreadStart [0x00007FFAD459CC91+33]\\n"}}'}

    def check_response(self, response: Dict[str, Any]) -> None:
        """Checks that a JSON response from the WebDriver does not have an
        error.
    
        :Args:
         - response - The JSON response from the WebDriver server as a dictionary
           object.
    
        :Raises: If the response contains an error message.
        """
        status = response.get("status", None)
        if not status or status == ErrorCode.SUCCESS:
            return
        value = None
        message = response.get("message", "")
        screen: str = response.get("screen", "")
        stacktrace = None
        if isinstance(status, int):
            value_json = response.get("value", None)
            if value_json and isinstance(value_json, str):
                import json
    
                try:
                    value = json.loads(value_json)
                    if len(value) == 1:
                        value = value["value"]
                    status = value.get("error", None)
                    if not status:
                        status = value.get("status", ErrorCode.UNKNOWN_ERROR)
                        message = value.get("value") or value.get("message")
                        if not isinstance(message, str):
                            value = message
                            message = message.get("message")
                    else:
                        message = value.get("message", None)
                except ValueError:
                    pass
    
        exception_class: Type[WebDriverException]
        e = ErrorCode()
        error_codes = [item for item in dir(e) if not item.startswith("__")]
        for error_code in error_codes:
            error_info = getattr(ErrorCode, error_code)
            if isinstance(error_info, list) and status in error_info:
                exception_class = getattr(ExceptionMapping, error_code, WebDriverException)
                break
        else:
            exception_class = WebDriverException
    
        if not value:
            value = response["value"]
        if isinstance(value, str):
            raise exception_class(value)
        if message == "" and "message" in value:
            message = value["message"]
    
        screen = None  # type: ignore[assignment]
        if "screen" in value:
            screen = value["screen"]
    
        stacktrace = None
        st_value = value.get("stackTrace") or value.get("stacktrace")
        if st_value:
            if isinstance(st_value, str):
                stacktrace = st_value.split("\n")
            else:
                stacktrace = []
                try:
                    for frame in st_value:
                        line = frame.get("lineNumber", "")
                        file = frame.get("fileName", "<anonymous>")
                        if line:
                            file = f"{file}:{line}"
                        meth = frame.get("methodName", "<anonymous>")
                        if "className" in frame:
                            meth = f"{frame['className']}.{meth}"
                        msg = "    at %s (%s)"
                        msg = msg % (meth, file)
                        stacktrace.append(msg)
                except TypeError:
                    pass
        if exception_class == UnexpectedAlertPresentException:
            alert_text = None
            if "data" in value:
                alert_text = value["data"].get("text")
            elif "alert" in value:
                alert_text = value["alert"].get("text")
            raise exception_class(message, screen, stacktrace, alert_text)  # type: ignore[call-arg]  # mypy is not smart enough here
>       raise exception_class(message, screen, stacktrace)
E       selenium.common.exceptions.ElementNotInteractableException: Message: element not interactable
E         (Session info: chrome=127.0.6533.120)
E       Stacktrace:
E       	GetHandleVerifier [0x00007FF7BD77EEA2+31554]
E       	(No symbol) [0x00007FF7BD6F7ED9]
E       	(No symbol) [0x00007FF7BD5B8559]
E       	(No symbol) [0x00007FF7BD6097C2]
E       	(No symbol) [0x00007FF7BD5FC151]
E       	(No symbol) [0x00007FF7BD62D02A]
E       	(No symbol) [0x00007FF7BD5FBA76]
E       	(No symbol) [0x00007FF7BD62D240]
E       	(No symbol) [0x00007FF7BD64C977]
E       	(No symbol) [0x00007FF7BD62CDD3]
E       	(No symbol) [0x00007FF7BD5FA33B]
E       	(No symbol) [0x00007FF7BD5FAED1]
E       	GetHandleVerifier [0x00007FF7BDA88B1D+3217341]
E       	GetHandleVerifier [0x00007FF7BDAD5AE3+3532675]
E       	GetHandleVerifier [0x00007FF7BDACB0E0+3489152]
E       	GetHandleVerifier [0x00007FF7BD82E776+750614]
E       	(No symbol) [0x00007FF7BD70375F]
E       	(No symbol) [0x00007FF7BD6FEB14]
E       	(No symbol) [0x00007FF7BD6FECA2]
E       	(No symbol) [0x00007FF7BD6EE16F]
E       	BaseThreadInitThunk [0x00007FFAD25B7374+20]
E       	RtlUserThreadStart [0x00007FFAD459CC91+33]

..\venv_hjh\lib\site-packages\selenium\webdriver\remote\errorhandler.py:229: ElementNotInteractableException

环境

  • 你这等待是加在点击之前的,你把等待加载等待之后看看能打印出来句柄吗?
  • 你用其他的网站去试试,看一下能不能获取多个句柄

只能获取到一条句柄

  • 这段是官方文档的代码你可以试一下能否正常运行,在里面的话,他设置了一个
    显示等待,等待窗口能够切换。

我看你代码里面虽然有死等,但是不知道出现窗口在哪个步骤后面

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

    # Open URL
    driver.get("https://seleniumhq.github.io")

    # Setup wait for later
    wait = WebDriverWait(driver, 10)

    # Store the ID of the original window
    original_window = driver.current_window_handle

    # Check we don't have other windows open already
    assert len(driver.window_handles) == 1

    # Click the link which opens in a new window
    driver.find_element(By.LINK_TEXT, "new window").click()

    # Wait for the new window or tab
    wait.until(EC.number_of_windows_to_be(2))

    # Loop through until we find a new window handle
    for window_handle in driver.window_handles:
        if window_handle != original_window:
            driver.switch_to.window(window_handle)
            break

    # Wait for the new tab to finish loading content
    wait.until(EC.title_is("SeleniumHQ Browser Automation"))
  

感谢 ,今天也重新换了台电脑,可能之前电脑也太卡了。不过有点奇怪 强制10秒能正常打印切换后的窗口,但是使用显示等待给30秒还是会报超时(两个时间的位置都是同一个位置,显示等待是根据切换后的窗口页面上的元素可见)

应该是代码写的有问题,可以发出来看看

如果我的理解没错,你的逻辑是:

  1. 显示等待新窗口某个元素加载。
  2. 点击切换新窗口

如果这样设计是不行的。应该换成:

  1. 点击切换新窗口
  2. 显示等待新窗口标题为xxx,对应的也就是 : wait.until(EC.title_is("SeleniumHQ Browser Automation"))

好像确实逻辑顺序反了,我再优化下,谢谢老师。
另外请教下,对于在公司里从0到1的去建设推广自动化的测试,从流程和价值方面,对于实战经验不是很多的人员来说,有没有一些好的建议?(简而言之就是怎么更好的去做自动化,让公司领导觉得这方面的投入不会浪费成本)

简而言之就是让你的同事和领导觉得你做的事情有价值。建议是先从接口开始做。辅助业务做一些造数的脚本

好的 谢谢

老师 再麻烦请教下 针对上面的脚本,我使用PO的方式进行封时遇到了一个问题,就是我在完成商机报送的时候,需要在当前页面获取商机编号并赋值给变量,然后在切换至新窗口流程审批时需要作为参数传递到搜索框,这种应该怎么实现跨方法去传递我在新窗口下所需要的参数呢

实例变量呗

想到了一个方式,我把那个依赖变量的页面直接返回变量,就不返回页面对象了,然后在测试用例那里先获取到这个值,再到下面的用例里传递