最近在使用 appium2 编写安卓自动化,appium2 的会话配置中有一项 appium:noReset: 默认为 false,即启动应用默认重置应用状态。 为了不每次启动应用都要重新登陆然后点击各种弹窗,所以这一项我一般设置为 true。 那么问题来了,我初始化 driver 写在一个 fixture 里(代码最下面),我想每次结束一个条用例就会执行这个 fixture 的后置处理 即关闭应用,目的是在执行下一条用例时启动就会在首页(统一初始页面); 但是当 appium:noReset: true 时无论是 quit() 还是 close() 都会失败,好像是因为和 appium:noReset: true 产生冲突,即 noReset: true 不让你重置关闭也不行! ; 当然把 noReset: 设置为 false,倒是可以关闭了,但是下次启动应用直接重置应用了,要中登陆开始执行了!
语言组织得有点乱, 总的来说我的目的是:在不重置的应用的情况下关闭应用, 但是 appium2.0 是必须设置为每次重置应用才能让关闭应用方法生效!
这把我搞的好烦,求各位大佬指点迷津!
@pytest.fixture()
def init_driver(request):
# 前置
driver = webdriver.Remote(appium_server_url, options=AppiumOptions().load_capabilities(capabilities))
yield driver
# 后置
driver.close()
# driver.quit()
真的没人吗 555
现在用这的人少了,大部分都上云测,录脚本了
不能关闭,执行这条 driver.close()
报错是咋样的?
把你的 capabilities 贴出来看看
这是我的 caps 其实主要就是 appium:noReset: false
test_case/test_login.py:18 (TestLogin.test_login[RenphoHealth-account_data0])
request = <SubRequest 'init_driver' for <Function test_login[RenphoHealth-account_data0]>>
@pytest.fixture()
def init_driver(request):
# 前置
driver = webdriver.Remote(appium_server_url, options=AppiumOptions().load_capabilities(capabilities))
# 使用Allure的attach方法将配置信息作为文本附件添加到报告中
attach_name = "Appium Configuration"
attach_content = "\n".join([f"{key}: {value}" for key, value in capabilities.items()])
allure.attach(attach_content, name=attach_name, attachment_type=allure.attachment_type.TEXT)
yield driver
# 后置
> driver.close()
conftest.py:53:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/selenium/webdriver/remote/webdriver.py:459: in close
self.execute(Command.CLOSE)
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/selenium/webdriver/remote/webdriver.py:348: in execute
self.error_handler.check_response(response)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <appium.webdriver.errorhandler.MobileErrorHandler object at 0x1037ed990>
response = {'status': 404, 'value': '{"value":{"error":"unknown command","message":"The requested resource could not be found, or...tory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144)\\n\\tat java.lang.Thread.run(Thread.java:1012)\\n"}}'}
def check_response(self, response: Dict[str, Any]) -> None:
"""
https://www.w3.org/TR/webdriver/#errors
"""
payload = response.get('value', '')
if isinstance(payload, dict):
payload_dict = payload
else:
try:
payload_dict = json.loads(payload)
except (json.JSONDecodeError, TypeError):
return
if not isinstance(payload_dict, dict):
return
value = payload_dict.get('value')
if not isinstance(value, dict):
return
error = value.get('error')
if not error:
return
message = value.get('message', error)
stacktrace = value.get('stacktrace', '')
# In theory, we should also be checking HTTP status codes.
# Java client, for example, prints a warning if the actual `error`
# value does not match to the response's HTTP status code.
exception_class: Type[sel_exceptions.WebDriverException] = ERROR_TO_EXC_MAPPING.get(
error, sel_exceptions.WebDriverException
)
if exception_class is sel_exceptions.WebDriverException and message:
if message == 'No such context found.':
exception_class = appium_exceptions.NoSuchContextException
elif message == 'That command could not be executed in the current context.':
exception_class = appium_exceptions.InvalidSwitchToTargetException
if exception_class is sel_exceptions.UnexpectedAlertPresentException:
raise sel_exceptions.UnexpectedAlertPresentException(
msg=message,
stacktrace=format_stacktrace(stacktrace),
alert_text=value.get('data'),
)
> raise exception_class(msg=message, stacktrace=format_stacktrace(stacktrace))
E selenium.common.exceptions.UnknownMethodException: Message: The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource
E Stacktrace:
E io.appium.uiautomator2.common.exceptions.UnknownCommandException: The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource
E at io.appium.uiautomator2.http.ServerHandler.channelRead(ServerHandler.java:84)
E at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
E at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
E at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:345)
E at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
E at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
E at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
E at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:345)
E at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:435)
E at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:293)
E at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:267)
E at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:250)
E at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
E at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
E at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:345)
E at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:266)
E at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
E at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
E at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:345)
E at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1294)
E at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
E at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
E at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:911)
E at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:131)
E at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:611)
E at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:552)
E at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:466)
E at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:438)
E at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:140)
E at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144)
E at java.lang.Thread.run(Thread.java:1012)
你这里面,明显是 close 这个方法是没有的呀,参考:https://github.com/appium/python-client/blob/master/appium/webdriver/mobilecommand.py
这里面没有 close.
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/selenium/webdriver/remote/webdriver.py:459: in close
self.execute(Command.CLOSE)
你用的是的 remote,这里是有 CLOSE 的,参见:https://github.com/SeleniumHQ/selenium/blob/trunk/py/selenium/webdriver/remote/command.py
这里其实是对不上的,比较奇怪。不过 appium python client 里面用 remote 也只是用了 NEW_SESSION、FIND_ELEMENT(s)、GET_ELEMENT_ATTRIBUTE、FIND_CHILD_ELEMENT(s)、SEND_KEYS_TO_ELEMENT。本质上 appium-uiautomator2-server 里面没有实现那就应该没有了。
quit 倒是有,
remote 里面:
Command.QUIT: ("DELETE", "/session/$sessionId"),
uiautomator2 driver 里面 里面:
async deleteSession () {
this.log.debug('Deleting UiAutomator2 server session');
// rely on jwproxy's intelligence to know what we're talking about and
// delete the current session
try {
await this.jwproxy.command('/', 'DELETE');
} catch (err) {
this.log.warn(`Did not get confirmation UiAutomator2 deleteSession worked; ` +
`Error was: ${err}`);
}
}
@Override
public void handleHttpRequest(IHttpRequest request, IHttpResponse response) {
BaseRequestHandler handler = null;
if ("GET".equals(request.method())) {
handler = findMatcher(request, getHandler);
} else if ("POST".equals(request.method())) {
handler = findMatcher(request, postHandler);
} else if ("DELETE".equals(request.method())) {
handler = findMatcher(request, deleteHandler);
}
if (handler != null) {
handleRequest(request, response, handler);
}
}
driver.close() 是 selenium 的用法
driver.close_app() 才是 appium 的用法, 但是这个方法在 appium2.0 逐渐被废弃
driver.terminate_app("com.xxx.xxx") 这个是官方推荐的终止 APP 方法
这个问题确实如你所说,我用其他方式处理了, 顺便想问下大佬最近有没有用过 appium2,感觉好容易闪退啊,不知道有没有遇到过
话说:driver.terminate_app("com.xxx.xxx") 这个是官方推荐的终止 APP 方法,, 这些信息大佬都是在哪里看到的
?