java 自动化 元素识别,让 dogtail 识别 UI 中的元素

d2bc265ce82774d1970be2a4dcacf250.gif

在《利用 dogtail 快速进行 GUI 自动化测试》一节中,我们实现了一个最基础的自动化测试程序 - 模拟鼠标点击按钮。现在是时候更进一步了,开始访问 UI 元素中的信息。

假设,要获取按钮上的文本(例如:用于断言测试),修改之前的脚本:

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

from dogtail.tree import *

import time

# 获取应用程序(根据程序名称查找)

app = root.application(appName="Sample02", description="/home/waleon/workspace/demos/build-Samples-unknown-Debug/Sample02/Sample02")

# 获取按钮(根据 accessibleName 递归查找)

button = app.child('button')

# 获取并打印按钮上的文本

print('text:', button.text)

# 模拟鼠标点击

for i in range(3):

button.click()

sleep(1)

运行之后,你会发现 print() 打印的永远是 None。这是什么情况,按钮上分明有内容“test”,但为什么打印不出来?

imgconvert.csdnimg.cn

这是因为,按钮元素的信息没有对外公开,以至于 dogtail 无法识别出来!

不妨用 sniff 工具查看一下,定位到我们的程序并选择该按钮,点击底下的“Text”选项:

9758e1874016e6de3107d2c387c5a407.png

没有任何内容,这就说明按钮的文本是不可访问的。

1

实现可访问性

要为 UI 元素提供可访问性支持,需要使用 Qt 的 Accessibility 技术。即应实现 QAccessibleInterface,分发的形式有两种:

实现可访问的插件:子类化 QAccessiblePlugin,重新实现纯虚函数 create(),并使用 Q_PLUGIN_METADATA() 宏导出该类(如果想在运行时按需加载,则将接口作为插件分发比较方便)。

将接口编译到应用程序中:使用接口工厂 - QAccessible::InterfaceFactory(如果是静态链接,或不想增加插件的复杂性,则推荐该方式)。

下面以接口工厂为例,来实现对按钮文本的可访问性(要对文本进行操作,需要用到 QAccessibleTextInterface):

#ifndef ACCESSIBLE_BUTTON_H

#define ACCESSIBLE_BUTTON_H

#include 

#include 

#include 

class AccessibleButton : public QAccessibleWidget

, public QAccessibleTextInterface

{

public:

explicit AccessibleButton(QPushButton *button);

~AccessibleButton();

void *interface_cast(QAccessible::InterfaceType t) Q_DECL_OVERRIDE;

QString text(QAccessible::Text t) const Q_DECL_OVERRIDE;

QString text(int startOffset, int endOffset) const Q_DECL_OVERRIDE;

void selection(int selectionIndex, int *startOffset, int *endOffset) const Q_DECL_OVERRIDE;

int selectionCount() const Q_DECL_OVERRIDE;

void addSelection(int startOffset, int endOffset) Q_DECL_OVERRIDE;

void removeSelection(int selectionIndex) Q_DECL_OVERRIDE;

void setSelection(int selectionIndex, int startOffset, int endOffset) Q_DECL_OVERRIDE;

int cursorPosition() const Q_DECL_OVERRIDE;

void setCursorPosition(int position) Q_DECL_OVERRIDE;

int characterCount() const Q_DECL_OVERRIDE;

QRect characterRect(int offset) const Q_DECL_OVERRIDE;

int offsetAtPoint(const QPoint &point) const Q_DECL_OVERRIDE;

void scrollToSubstring(int startIndex, int endIndex) Q_DECL_OVERRIDE;

QString attributes(int offset, int *startOffset, int *endOffset) const Q_DECL_OVERRIDE;

private:

QPushButton *m_button;

};

#endif // ACCESSIBLE_BUTTON_H

由于访问的是按钮的文本,所以重点关注 interface_cast()、text() 即可,其他接口可以“忽略”:

#include "accessiblebutton.h"

#include 

AccessibleButton::AccessibleButton(QPushButton *button)

: QAccessibleWidget(button)

, m_button(button)

{

}

AccessibleButton::~AccessibleButton()

{

}

void *AccessibleButton::interface_cast(QAccessible::InterfaceType t)

{

switch (t) {

case QAccessible::ActionInterface:

return static_cast(this);

case QAccessible::TextInterface:

return static_cast(this);

default:

return nullptr;

}

}

QString AccessibleButton::text(QAccessible::Text t) const

{

switch (t) {

case QAccessible::Name:

return m_button->accessibleName();

case QAccessible::Description:

return m_button->accessibleDescription();

default:

return QString();

}

}

QString AccessibleButton::text(int startOffset, int endOffset) const

{

Q_UNUSED(startOffset)

Q_UNUSED(endOffset)

return m_button->text();

}

void AccessibleButton::selection(int selectionIndex, int *startOffset, int *endOffset) const

{

Q_UNUSED(selectionIndex)

Q_UNUSED(startOffset)

Q_UNUSED(endOffset)

}

int AccessibleButton::selectionCount() const

{

return 0;

}

void AccessibleButton::addSelection(int startOffset, int endOffset)

{

Q_UNUSED(startOffset)

Q_UNUSED(endOffset)

}

void AccessibleButton::removeSelection(int selectionIndex)

{

Q_UNUSED(selectionIndex)

}

void AccessibleButton::setSelection(int selectionIndex, int startOffset, int endOffset)

{

Q_UNUSED(selectionIndex)

Q_UNUSED(startOffset)

Q_UNUSED(endOffset)

}

int AccessibleButton::cursorPosition() const

{

return 0;

}

void AccessibleButton::setCursorPosition(int position)

{

Q_UNUSED(position)

}

int AccessibleButton::characterCount() const

{

return 0;

}

QRect AccessibleButton::characterRect(int offset) const

{

Q_UNUSED(offset)

return QRect();

}

int AccessibleButton::offsetAtPoint(const QPoint &point) const

{

Q_UNUSED(point)

return 0;

}

void AccessibleButton::scrollToSubstring(int startIndex, int endIndex)

{

Q_UNUSED(startIndex)

Q_UNUSED(endIndex)

}

QString AccessibleButton::attributes(int offset, int *startOffset, int *endOffset) const

{

Q_UNUSED(offset)

Q_UNUSED(startOffset)

Q_UNUSED(endOffset)

return QString();

}

2

实现接口工厂

工厂是一个函数指针,其签名如下:

typedef QAccessibleInterface* myFactoryFunction(const QString &key, QObject *);

该函数接收一个 QString 和一个 QObject 指针,其中 QString 是标识接口的键。QObject 用于传递到 QAccessibleInterface,以便它可以保存对其的引用。

注意:如果 key 和 QObject 没有对应的 QAccessibleInterface,将返回一个空指针。

来看一下我们要实现的工厂示例:

// 接口工厂

QAccessibleInterface *accessibleFactory(const QString &classname, QObject *object)

{

QAccessibleInterface *interface = nullptr;

if (classname == "QPushButton" && object && object->isWidgetType())

interface = new AccessibleButton(static_cast(object));

return interface;

}

3

安装工厂

完成之后,需要使用 QAccessible::installFactory 对上述工厂进行安装:

int main(int argc, char *argv[])

{

QApplication a(argc, argv);

// 安装工厂

QAccessible::installFactory(accessibleFactory);

QPushButton button("test");

QObject::connect(&button, &QPushButton::clicked, [&]() {

static int index = 0;

index++;

button.setText(QString("click %1").arg(index));

});

// 将被辅助技术识别

button.setAccessibleName("button");

button.setAccessibleDescription("this is a simple button");

button.resize(300, 200);

button.show();

return a.exec();

}

4

执行自动化

运行程序,再使用 sniff 工具查看一下,这时已经可以检测出按钮的文本了:

3f39638cd172a7afd9a79f64182fa3f7.png

然后重新运行下自动化脚本,文本也可以正常打印了:

30b005504220f855976c84f7359a1883.png

恭喜,这说明 dogtail 已经可以成功识别 UI 中的元素了。

最后,在自动化测试的过程中,有可能还会有其他元素的信息无法识别,参考上述方式即可。

·END·

abc29bf8cbb634bd84f9d7925db872db.gif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值