从单个测试脚本测试多个AUT,使用ApplicationContext

通常,每个测试套件都指定一个待测应用程序。然后,通过每个测试用例执行和访问这个AUT。所有教程都展示了这个测试套件/单个AUT的方法,但你也可以从单个测试套件中启动多个应用程序,并访问和测试它们。这使得测试不同应用程序之间的交互,或是同一应用程序的多实例之间的交互成为可能。例如,对客户端/服务器系统进行测试时,能够测试多个应用程序是必不可少的。

每次启动AUT时,都会创建一个相应的应用程序上下文对象,Squish就是通过这个对象来提供对AUT访问的。Squish允许我们直接在我们的代码中访问ApplicationContext对象,这意味着我们可以查询AUT的信息,例如它启动时的命令行、当前状态等。这些信息还可以通过使用ApplicationContext currentApplicationContext()函数返回的上下文对象来访问。

如何启动和访问多个待测应用程序

从单个测试脚本测试多个应用程序时,第一步是确保没有应用程序设置为自动启动。使用squishide,点击测试套件视图中的测试套件设置工具栏按钮,使测试套件的测试套件设置视图可见。现在,在编辑器的待测应用程序(AUT)部分,确保自动启动AUT复选框是未选中的。

启动应用程序使用的函数是ApplicationContext startApplication(autName)。此函数使用给定的命令行参数启动指定应用程序(假设它位于应用程序路径中——见AUTs and Settings),并返回相应的ApplicationContext对象。应用程序上下文对象是一个引用应用程序的句柄。

可选地,可以作为第二个和第三个参数传递主机和端口到ApplicationContext startApplication(autName)函数。这样,ApplicationContext startApplication(autName)函数将连接到指定主机的squishserver并监听指定端口,而不是使用默认的主机和端口(如squishide的设置或squishrunner的命令行中指定)。这允许我们从单个测试脚本控制多台计算机上的多个应用程序。

如果应用程序使用与测试套件默认工具包不同的GUI工具包,必须特别注意。全局的testSettings对象允许我们根据每个AUT设置工具包包装器的配置。有关更多信息,请参阅testSettings.setWrappersForApplication(application, wrapperList)

如果在测试脚本中运行两个或多个AUT,应该将测试代码应用到哪个AUT上?我们可以通过使用setApplicationContext(contextHandle)函数,将其中一个AUT设为活动应用程序,将ApplicationContext作为唯一参数传递。一旦调用成功,所有脚本代码都应用于活动应用程序——除非使用另一个setApplicationContext(contextHandle)调用更改活动应用程序。每次调用ApplicationContext startApplication(autName)函数时,不仅会返回应用程序的ApplicationContext对象,而且应用程序会自动设置为活动应用程序。

通过调用SequenceOfApplicationContexts applicationContextList()函数,我们可以获取所有当前运行AUT的ApplicationContext对象的列表。我们可以通过调用ApplicationContext currentApplicationContext()函数检索活动应用程序的上下文对象。

注意:要记录和访问由AUT自身启动而不是由Squish启动的应用程序,请参阅Record / Replay on Sub-Processes started by the AUT

现在我们将查看一些示例,说明如何启动多个AUT以及如何使用ApplicationContext对象查询它们。

我们将以一个客户端/服务器聊天系统为例。该系统有一个名为chatserver的聊天服务器,用Qt编写,必须运行通信才能进行,还有两个聊天客户端,一个用Qt编写,名为chatclientqt,另一个在Windows上编写,名为chatclientwin。我们将使用Squish for Qt包,该包包括Qt和原生Windows应用程序的包装器以及Qt测试套件。

在测试中,我们首先启动聊天服务器。然后启动两个客户端;这些客户端在启动时会自动连接到聊天服务器。然后我们将在第一个客户端的消息编辑器中输入一些文字并检查第二个客户端是否接收到了这条消息。

startApplication("chatserver")
client1 = startApplication("chatclientqt")
testSettings.setWrappersForApplication("chatclientwin", ["Windows"])
client2 = startApplication("chatclientwin")

setApplicationContext(client1)
editor = waitForObject("ChatWindow.messageEditor")
type(editor, "Message for client #2")

setApplicationContext(client2)
msgView = waitForObject("ChatWindow.messageView")
test.compare(msgView.text, "Message for client #2")
startApplication("chatserver");
var client1 = startApplication("chatclientqt");
testSettings.setWrappersForApplication("chatclientwin", ["Windows"]);
var client2 = startApplication("chatclientwin");

setApplicationContext(client1);
var editor = waitForObject("ChatWindow.messageEditor");
type(editor, "Message for client #2");

setApplicationContext(client2);
var msgView = waitForObject("ChatWindow.messageView");
test.compare(msgView.text, "Message for client #2");
startApplication("chatserver");
my $client1 = startApplication("chatclientqt");
testSettings->setWrappersForApplication("chatclientwin", ("Windows"));
my $client2 = startApplication("chatclientwin");

setApplicationContext($client1);
my $editor = waitForObject("ChatWindow.messageEditor");
type($editor, "Message for client #2");

setApplicationContext($client2);
my $msgView = waitForObject("ChatWindow.messageView");
test::compare($msgView->text, "Message for client #2");
startApplication("chatserver")
client1 = startApplication("chatclientqt")
testSettings.setWrappersForApplication("chatclientwin", ("Windows"))
client2 = startApplication("chatclientwin")

setApplicationContext(client1)
editor = waitForObject("ChatWindow.messageEditor")
type(editor, "Message for client #2")

setApplicationContext(client2)
msgView = waitForObject("ChatWindow.messageView")
Test.compare(msgView.text, "Message for client #2")
startApplication "chatserver"
set client1 [startApplication "chatclientqt"]
testSettings setWrappersForApplication chatclientwin { Windows }
set client2 [startApplication "chatclientwin"]

setApplicationContext $client1
set editor [waitForObject "ChatWindow.messageEditor"]
invoke type $editor "Message for client #2"

setApplicationContext $client2
set msgView [waitForObject "ChatWindow.messageView"]
test compare [property get $msgView text] "Message for client #2"

首先我们依次启动每个应用程序,尽管我们只保留了客户端AUT的ApplicationContext对象的引用,因为我们测试中不直接访问服务器。对于Windows客户端,我们将工具包包装器设置为使用Windows包装器而不是默认的Qt包装器。一旦应用程序运行,我们将第一个客户端设为活动AUT,因为当前的活动AUT是client2,这是我们最近的ApplicationContext startApplication(autName)调用启动的AUT。然后我们获取客户端聊天编辑器的引用,向其中输入一些文本。最后,我们将第二个客户端设为活动AUT,获取其聊天编辑器的引用(这次由于工具包不同——Java而不是Qt),并将第二个客户端编辑器的文本与从第一个客户端发送的文本进行比较。

使用startApplication的注意事项

对于Android,ApplicationContext startApplication(autName)函数中的autName是包名。它后面可以跟一个斜杠加上活动。如果没有指定活动,则启动包的主活动,否则从给定的活动开始。

当活动是短横线时,那么应用程序会被启动,但没有活动会被启动。当您的AUT使用来自其他包的活动时,使用此选项。

最后,包名可能以设备字符串为前缀,后跟一组启动器参数设置,以及冒号。因此,当指定一切时,自动命名看起来像device{option1,...}:package/activity,没有选项时像device:package/activity。同样,当需要选项但设备是默认的(由squishide或squishrunner提供)时,可以省略device

注意:在使用testSettings.setWrappersForApplication(application, wrapperList)时,application参数必须与用于ApplicationContext startApplication(autName)autName相同。

例如,如果只想在Squish启动时清除多个启动的应用程序中的一个的设置,可以将在应用程序字符串中使用–clear-app-settings启动器参数。例如:startApplication("{clear-app-settings}:com.froglogic.addressbook")

注意:由Squish启动的包必须在测试套件设置页面中点击新建进行仪器化,例如。Squish会自动创建和安装此类仪器包。另请参阅为Android安装Squish

我们将以一个包com.example.android.tools为例,这个包从一个包com.example.android.carpenter启动活动,当点击按钮时。工具选择将自动关闭活动,我们回到主活动。

ctx0 = startApplication("com.example.android.tools/-")
ctx1 = startApplication("com.example.android.carpenter")
tapObject(waitForObject(":Choose Tool_Button")
setApplicationContext(ctx0)
tapObject(waitForObjectItem("_List", "Hammer"))
setApplicationContext(ctx1)
var ctx0 = startApplication("com.example.android.tools/-");
var ctx1 = startApplication("com.example.android.carpenter");
tapObject(waitForObject(":Choose Tool_Button");
setApplicationContext(ctx0);
tapObject(waitForObjectItem("_List", "Hammer"));
setApplicationContext(ctx1);
my $ctx0 = startApplication("com.example.android.tools/-");
my $ctx1 = startApplication("com.example.android.carpenter");
tapObject(waitForObject(":Choose Tool_Button");
setApplicationContext(ctx0);
tapObject(waitForObjectItem("_List", "Hammer"));
setApplicationContext(ctx1);
ctx0 = startApplication("com.example.android.tools/-")
ctx1 = startApplication("com.example.android.carpenter")
tapObject(waitForObject(":Choose Tool_Button")
setApplicationContext(ctx0)
tapObject(waitForObjectItem("_List", "Hammer"))
setApplicationContext(ctx1)
set ctx0 [startApplication "com.example.android.tools/-"]
set ctx1 [startApplication "com.example.android.carpenter"]
invoke tapObject [waitForObject ":Choose Tool_Button"]
setApplicationContext $ctx0
invoke tapObject [waitForObjectItem "_List", "Hammer"]
setApplicationContext $ctx1

如何使用ApplicationContext对象

使用ApplicationContext对象来检索关于它引用的AUT的信息。测试套件设置中定义的AUT的应用程序上下文可以用ApplicationContext defaultApplicationContext()函数检索,当前正在运行的AUT可以用ApplicationContext currentApplicationContext()函数检索。当有多个AUT启动时,测试套件设置中不应定义任何AUT。每个AUT的上下文对象可以作为调用ApplicationContext startApplication(autName)函数的返回值检索,该函数用于启动AUT,也可以从SequenceOfApplicationContexts applicationContextList()函数中检索,该函数返回所有AUT的上下文对象。

应用程序上下文部分详细说明了从ApplicationContext对象可访问的属性和函数。以下是一些示例。

ctx = currentApplicationContext()
test.log(ctx.commandLine)
test.log(ctx.cwd)
var ctx = currentApplicationContext();
test.log(ctx.commandLine);
test.log(ctx.cwd);
my $ctx = currentApplicationContext();
test::log($ctx->commandLine);
test::log($ctx->cwd);
ctx = currentApplicationContext()
Test.log(ctx.commandLine)
Test.log(ctx.cwd)
set ctx [currentApplicationContext]
test log [applicationContext $ctx commandLine]
test log [applicationContext $ctx cwd]

这里我们打印了AUT调用的命令行和当前工作目录 - 这些都是属性。

ctx = currentApplicationContext()
peakMemory = 0
while ctx.isRunning:
    peakMemory = max(ctx.usedMemory, peakMemory)
    if not ctx.isFrozen(20):
    break
test.log("Peak Memory: %d" % peakMemory)
var ctx = currentApplicationContext();
var peakMemory = 0;
while (ctx.isRunning) {
    peakMemory = Math.max(ctx.usedMemory, peakMemory);
    if (!ctx.isFrozen(20))
    break;
}
test.log("Peak Memory: " + peakMemory);
my $ctx = currentApplicationContext();
my $peakMemory = 0;
while ($ctx->isRunning) {
    if ($ctx->usedMemory > $peakMemory) {
    $peakMemory = $ctx->usedMemory;
    }
    if (!$ctx->isFrozen(20)) {
    last;
    }
}
test::log("Peak Memory: $peakMemory")
ctx = currentApplicationContext()
peakMemory = 0
while ctx.isRunning
    peakMemory = ctx.usedMemory > peakMemory ? ctx.usedMemory : peakMemory
    if !ctx.isFrozen(20)
    break
    end
end
Test.log("Peak Memory: #{peakMemory}")
set ctx [currentApplicationContext]
set peakMemory 0
while {[applicationContext $ctx isRunning] == 1} {
    if {[applicationContext $ctx usedMemory] > $peakMemory} {
    set peakMemory [applicationContext $ctx usedMemory]
    }
    if {![applicationContext $ctx isFrozen 20]} {
    break
    }
}
test log "Peak Memory: $peakMemory"

这里我们访问当前运行的AUT并跟踪它使用的最大内存量。如果在等待20秒后应用程序停止运行(在这种情况下isRunning将变为false),或应用程序变得无响应(冻结),我们将退出循环。

ctx = currentApplicationContext()
test.log("STDOUT", ctx.readStdout())
test.warning("STDERR", ctx.readStderr())
var ctx = currentApplicationContext();
test.log("STDOUT", ctx.readStdout());
test.warning("STDERR", ctx.readStderr());
my $ctx = currentApplicationContext();
test::log("STDOUT", $ctx->readStdout());
test::warning("STDERR", $ctx->readStderr());
ctx = currentApplicationContext()
Test.log("STDOUT", ctx.readStdout())
Test.warning("STDERR", ctx.readStderr())
set ctx [currentApplicationContext]
test log "STDOUT" [applicationContext $ctx readStdout]
test warning "STDERR" [applicationContext $ctx readStderr]

我们将AUT写入stdoutstderr的所有内容都添加到测试日志中,并将所有stderr消息分类为警告。

©2024 Qt公司有限公司。所包含的文档贡献是各自所有者的版权。
提供的文档根据自由软件基金会发布的GNU自由文档许可第1.3版的条款进行许可。
Qt 及其相应标志是芬兰 Qt Company Ltd. 及/或其他国家/地区的世界商标。所有其他商标均为其各自所有者所有。