如何从Squish测试脚本访问数据库

Squish测试脚本可以访问由底层脚本语言提供的适合库的数据库。(在JavaScript的情况下,由于没有这样的库,Squish提供了一个——参见SQL。)

您可以使用数据库向测试脚本提供输入或验证数据,或者将测试结果直接记录到数据库中。以下各节中提供了这两种使用方法的示例。

Python特定的

Squish的二进制发布版包括Python精简版本,不包括一些标准库,如ssl、sqlite3、pip、mysql和odbc。要从Squish测试访问数据库,必须使用包含Python完整安装的Squish。您可以替换现有安装所使用的版本,或者在从源代码编译Squish时,在运行configure时指定--with-python=/path/to/full/python

本节中的示例使用SQLite 3和自Python 2.5以来的标准的Python库中的sqlite3包提供的绑定。

顺便提一下,PyPI(Python包索引)提供了许多不同的数据库绑定软件包,可以使用pip安装它们,因此在Python中使用时,您不必局限于标准库。

Perl特定的

本节中的示例使用SQLite 3,这些绑定由从CPAN可用的DBD::SQLite软件包提供。

Windows用户可以通过启动控制台会话,并在命令行中使用perl -MCPAN -e shell来安装此软件包(这假定该机器连接到互联网)。这将生成CPAN提示符,您必须安装两个软件包。首先输入install DBI,然后输入install DBD::SQLite。对于使用系统的Perl安装(而不是Squish提供的Perl)的Unix-like系统用户,请使用包管理工具安装DBI和DBD::SQLite软件包。

确保使用与Squish相同的Perl安装这些软件包(例如,Squish目录中的那个)。如果您使用的是与Squish二进制软件包一起提供的Perl,这可能会不起作用;在这些情况下,请联系Qt技术支持中心

Tcl特定的

本节中的示例使用SQLite 3,由SQLite开发者提供的绑定。

Linux用户应该能够通过他们的包管理工具获得绑定——软件包名称应该是tclsqlite或类似——假定您使用的是系统的Tcl而不是与Squish一起提供的Tcl。Windows用户需要从SQLite下载绑定。单击“下载”链接并获取二进制软件包tclsqlite-version.zip。macOS用户可能需要从源代码构建包——就像在 clicked the “Download”链接后可从SQLite获得的Windows二进制软件包一样。

如何比较应用程序数据和数据库数据

有时将应用程序数据与数据库中的数据进行比较非常方便。一些脚本语言在其标准库中包含了数据库访问模块。不幸的是,JavaScript 并非如此,因此 Squish 提供了SQL 对象,该对象可从 JavaScript 测试脚本中与数据库进行交互。

在本小节,我们将探讨如何从表小部件中读取数据,并对每一行检查每个单元格是否与相应 SQL 数据库行中的字段具有相同的数据。在示例中,我们将使用 Java AWT/Swing 的 JTable 作为数据承载小部件,但当然,我们也可以使用相同的方法使用 Java SWT 表或 Qt QTableWidget,或任何其他支持的工具包的表。

我们的 main 函数的结构与我们之前在 CsvTable 示例中使用的结构非常相似,在 CsvTable 示例中,我们比较了 JTable 的内容和填充表的 .csv 文件的内容。然而,这次而不是使用自定义的 compareTableWithDataFile 函数,我们有一个 compareTableWithDatabase 函数。(参见 如何测试 JTable 和使用外部数据文件(Java—AWT/Swing) 如何测试表小部件和使用外部数据文件(Java/SWT) 如何测试表小部件和使用外部数据文件。)

def main():
    startApplication('"' + os.environ["SQUISH_PREFIX"] + '/examples/java/csvtable/CsvTableSwing.jar"')
    source(findFile("scripts", "common.py"))
    filename = "before.csv"
    doFileOpen(filename)
    jtable = waitForObject("{type='javax.swing.JTable' visible='true'}")
    compareTableWithDatabase(jtable)
function main()
{
    startApplication('"' + OS.getenv("SQUISH_PREFIX") + '/examples/java/csvtable/CsvTableSwing.jar"');
    source(findFile("scripts", "common.js"));
    var filename = "before.csv";
    doFileOpen(filename);
    var jtable = waitForObject(
        "{type='javax.swing.JTable' visible='true'}");
    compareTableWithDatabase(jtable);
}
sub main
{
    startApplication("\"$ENV{'SQUISH_PREFIX'}/examples/java/csvtable/CsvTableSwing.jar\"");
    source(findFile("scripts", "common.pl"));
    my $filename = "before.csv";
    doFileOpen($filename);
    my $jtable = waitForObject(
        "{type='javax.swing.JTable' visible='true'}");
    compareTableWithDatabase($jtable);
}
proc main {} {
    startApplication "\"$::env(SQUISH_PREFIX)/examples/java/csvtable/CsvTableSwing.jar\""
    source [findFile "scripts" "common.tcl"]
    set filename "before.csv"
    doFileOpen $filename
    set jtable [waitForObject {{type='javax.swing.JTable' visible='true'}}]
    compareTableWithDatabase $jtable
}

main 函数首先加载一些常用的便利函数,包括一个 doOpenFile 函数,该函数通过 AUT 的菜单系统导航以打开具有给定名称的文件。文件加载完成后,JTable 将用文件的内容进行填充,然后我们调用自定义的 compareTableWithDatabase 函数以确定从 .csv 文件加载的内容是否与 SQLite 3 数据库文件中的数据匹配。

遗憾的是,不同脚本语言之间的数据库 API 差异相当大,因此尽管自定义的 compareTableWithDatabase 函数的结构都相同,但细节稍有不同。鉴于这种情况,我们将单独在每个子小节中查看每种语言的实现情况——每个子小节本身都是完整的,因此您只需阅读与您感兴趣的脚本语言相关的子小节。

在 Python 中比较图形用户界面表与数据库表

import sqlite3
import os

def compareTableWithDatabase(jtable):
    db3file = findFile("testdata", "before.db3")
    db = cursor = None
    try:
        tableModel = jtable.getModel()
        db = sqlite3.connect(db3file)
        cursor = db.cursor()
        cursor.execute("SELECT id, pollutant, type, standard, "
            "averaging_time, regulatory_citation FROM csv ORDER BY id")
        for record in cursor:
            row = record[0] - 1
            for column in range(0, 5):
                test.compare(tableModel.getValueAt(row, column)
                             .toString(), record[column + 1])
    finally:
        if cursor is not None:
            cursor.close()
        if db is not None:
            db.close()

我们必须做的第一件事——在编写任何函数之前——是导入由 pysqlite 包提供的 sqlite3 模块。

要连接到 SQLite 数据库,我们只需要提供一个文件名。连接方式在各脚本语言和库之间可能有所不同,它们的 SQL API 也一样,但原则上是相同的,尽管语法细节可能会有所不同,尽管在大多数情况下,它们需要用户名、密码、主机名和端口号,而不是文件名。

在 Python 中,我们必须获取一个连接,然后使用该连接获取数据库 "游标"。我们通过该游标执行查询。在本例中,SQL 数据库表有一个不在 .csv 文件中存在的字段——id——实际上对应于记录的行(使用 1 基索引)。一旦我们获得连接和游标,我们就会获取 JTable 的底层模型的引用——自然地,如果我们使用不同的工具包,这会有所不同。但无论我们直接访问表小部件的单元格还是通过模型访问,我们仍然可以访问每个单元格的数据。然后我们执行 SELECT 查询。我们可以通过迭代游标(如果有返回的行)来遍历查询返回的行。

光标返回的每一行实际上是一个元组。我们首先检索记录的 id(这是记录元组的第一个元素),然后减去 1,因为 JTable 使用基于 0 的行号,而数据库使用基于 1 的 ID,这些 ID 对应于行号。然后我们遍历每一列,检索指定行和列的 JTable 文本,并将其与数据库中相应行(ID)和列的记录进行比较。(我们必须给数据库列加 1,因为数据库最前面有一个额外的列,用于存储 ID。)

最后,只要我们一开始就成功建立了连接,我们就关闭光标和数据库连接。虽然这对 SQLite 来说并不重要,但关闭其他数据库的连接通常非常重要,因此我们使用了 try ... finally 构造来确保无论连接之后发生什么,连接始终安全关闭。(当然,Squish 本身会帮我们关闭连接,但我们还是更喜欢采取最佳实践方法进行测试代码编写。)

在 JavaScript 中比较 GUI 表格和数据库表格

function compareTableWithDatabase(jtable)
{
    var db3file = findFile("testdata", "before.db3");
    var db;
    try {
        var tableModel = jtable.getModel();
        db = SQL.connect({Driver: "SQLite", Host: "localhost",
                          Database: db3file, UserName: "", Password: ""});
        var result = db.query("SELECT id, pollutant, type, standard, " +
            "averaging_time, regulatory_citation FROM csv ORDER BY id");
        while (result.isValid) {
            var row = result.value("id") - 1;
            for (var column = 0; column < 5; ++column)
                test.compare(tableModel.getValueAt(row, column)
                    .toString(), result.value(column + 1));
            result.toNext();
        }
    }
    finally {
        if (db)
            db.close();
    }
}

对于 SQLite 数据库,没有必要提供主机、用户名或密码,但在这里在 JavaScript 版本中我们这样做是因为它们几乎在所有其他数据库中都必不可少(尽管在大多数情况下,如果没有指定,主机将合理地默认为 localhost)。另一个 SQLite 特性是我们必须指定一个数据库名称。连接方式在不同的脚本语言和库之间有所不同,它们的 SQL API 也一样,但原理上是相同的,尽管语法细节可能会有所不同。

在 JavaScript 中使用 Squish 的 SQL 对象,我们可以在连接对象本身上执行查询。事实上,JavaScript API 有两种查询函数我们可以使用,一种是用于执行 SELECT 语句的 SQLResult sqlConnection.query(sql) 函数,另一种是用于所有其他 SQL 语句的 Number sqlConnection.execute(sql) 函数(例如,DELETEINSERTUPDATE)。

在这个特定的例子中,SQL 数据库表有一个在 .csv 文件中不存在的字段—id,它实际上对应于记录的行(但使用基于 1 的索引)。一旦我们建立了连接,我们就获得 JTable 底层数据模型的引用——当然,如果我们使用不同的工具包,这将不同,但无论我们是否直接访问表格小部件的单元格或通过模型访问,我们都可以访问每个单元格的数据。然后我们执行 SELECT 查询。查询返回一个 SQLResult 对象,这会自动导航到结果集中的第一个记录(假设有结果)。这使我们能够访问结果集中的第一个记录。

JavaScript API 的 SQLResult 对象isValid 属性为 true,如果我们已经导航到一个有效的记录。String sqlResult.value(fieldNumber|fieldName) 方法可以接受一个字段索引(在这个例子中,对于 id 字段是 0,对于 pollutant 字段是 1 等),或者字段名。我们首先使用字段名检索记录的 id,并减去 1 来考虑 JTable 使用 0 基准行而数据库使用 1 基准 ID 对应行的事实。然后我们对每个列进行迭代,检索给定行和列的 JTable 的文本,并将其与数据库记录中对应的行(ID)和列进行比较。)(我们必须向数据库列加 1,因为数据库在开头有一个额外的列用来存储 ID。)一旦比较了表中所有行的单元格与数据库记录的字段,我们就尝试使用 sqlResult.toNext() 方法在数据库中导航到下一个记录。

最后,如果我们最初成功建立连接的话,我们关闭数据库连接。虽然对于 SQLite 来说这并不重要,但关闭其他数据库的连接通常非常重要,因此我们使用了 try ... finally 结构来确保无论连接建立后发生什么,连接最终都能安全关闭。(当然,Squish 也会为我们关闭连接,但我们更喜欢采取最佳实践来处理测试代码。)

在 Perl 中比较 GUI 表与数据库表

require Encode;
use DBI;

sub compareTableWithDatabase
{
    my $jtable = shift(@_);
    my $db3file = findFile("testdata", "before.db3");
    eval {
        my $db = DBI->connect("dbi:SQLite:$db3file") ||
            die("Failed to connect: $DBI::errstr");
        my $tableModel = $jtable->getModel();
        my $records = $db->selectall_arrayref(
                "SELECT id, pollutant, type, standard, averaging_time, " .
                "regulatory_citation FROM csv ORDER BY id");
        foreach my $record (@$records) {
            my $row = $record->[0] - 1;
            foreach $column (0..4) {
                my $field = $record->[$column + 1];
                Encode::_utf8_on($field);
                test::compare($tableModel->getValueAt($row, $column)->
                    toString(), $field);
            }
        }
    };
    if ($@) {
        test::fatal("$@");
    }
    else {
        $db->disconnect;
    }
}

我们首先必须做的是——在写入任何函数之前——请求 Encode 模块(我们将在稍后解释为什么需要它),并使用提供 Perl 数据库访问功能的 DBI 模块。

要连接到 SQLite 数据库,我们只需要提供一个文件名。连接方式在各脚本语言和库之间可能有所不同,它们的 SQL API 也一样,但原则上是相同的,尽管语法细节可能会有所不同,尽管在大多数情况下,它们需要用户名、密码、主机名和端口号,而不是文件名。

在 Perl 中,我们必须获取一个连接,然后使用连接对象执行我们的数据库操作。在该具体示例中,SQL 数据库表有一个字段不在 .csv 文件中——id—实际上对应于记录的行(但使用 1 基准索引)。一旦我们有了连接,我们获取对 JTable 的底层模型的引用——当然,如果我们使用不同的工具包,这也会不同,但无论我们直接访问表小部件的单元格还是通过模型访问,我们都能获得对每个单元格数据的访问。然后我们执行 SELECT 查询,请求将结果作为结果数组的引用(而不是复制数组,这将效率低下)。我们可以通过迭代数组的项来迭代查询返回的行(如果有的话)。

每个数组元素都保存一个记录。我们首先检索记录的 id,这是记录的第一项,并减去 1 来考虑 JTable 使用 0 基准行而数据库使用 1 基准 ID 对应行的事实。然后我们对每个列进行迭代,检索给定行和列的 JTable 的文本,并将其与数据库记录中对应的行(ID)和列进行比较。(我们必须向数据库列加 1,因为数据库在开头有一个额外的列用来存储 ID。)

Java存储文本为Unicode,因此JTable也是这样操作的,我们SQLite 3数据库中的文本也是按照Unicode存储的(使用了UTF-8编码)。但是,Perl默认假定文本使用本地8位编码,所以当我们从每条记录中检索每个文本字段时,我们必须确保Perl知道它是以Unicode形式存储的,以便正确执行Unicode字符串之间的比较,而不是JTable中的Unicode字符串与数据库中(可能无效的)本地8位文本之间的比较。这是通过使用Encode模块的_utf8_on方法实现的。(请注意,只有在我们确定我们标记的字符串含有UTF-8文本时,才应使用此方法。)

最后,如果我们首先成功建立了数据库连接,则关闭与数据库的连接。尽管对于SQLite来说这并不重要,但关闭与其他数据库的连接通常非常重要,所以我们使用了一个eval块来确保无论连接之后发生什么,最终都会安全地关闭连接。(当然,Squish会为我们关闭连接,但我们更喜欢在测试代码中使用最佳实践。)

在Tcl中比较GUI表格和数据库表格

package require sqlite3

proc compareTableWithDatabase {jtable} {
    sqlite3 db [findFile "testdata" "before.db3"]
    set tableModel [invoke $jtable getModel]
    set fields [list pollutant type standard averaging_time \
        regulatory_citation]
    set row 0
    db eval {SELECT id, pollutant, type, standard, averaging_time, \
            regulatory_citation FROM csv ORDER BY id} values {
        for {set column 0} {$column < 5} {incr column} {
            set table_value [invoke [invoke $tableModel getValueAt \
                $row $column] toString]
            set db_value $values([lindex $fields $column])
            test compare $table_value $db_value
        }
        incr row
    }
    db close
}

我们必须做的第一件事——在我们编写任何函数之前——是导入sqlite3模块。如果包安装在标准位置,则可以使用packagerequire语句来导入。否则,需要加载包含绑定共享库——例如,通过将packagerequire语句替换为,例如load "C:\tclsqlite3.dll"

要连接到 SQLite 数据库,我们只需要提供一个文件名。连接方式在各脚本语言和库之间可能有所不同,它们的 SQL API 也一样,但原则上是相同的,尽管语法细节可能会有所不同,尽管在大多数情况下,它们需要用户名、密码、主机名和端口号,而不是文件名。

在Tcl中,我们可以通过连接对象执行所有的数据库操作。在此特定示例中,SQL数据库表有一个字段在.csv文件中不存在——id——它实际上对应于记录的行(但使用基于1的索引)。一旦我们获得了连接,就获取JTable的底层模型的引用——当然,如果我们使用不同的工具包,这会有所不同,但无论我们是通过表格小部件的单元格直接访问还是通过模型访问,我们仍然可以访问每个单元格的数据。然后我们执行SELECT查询。查询依次将每行返回到values数组中。

在此示例中,我们忽略了返回的每行的第一个字段(id),因为这个字段在JTable中不存在。对于其余的列,我们对每个列进行迭代,检索给定行和列的JTable的文本,并将其与相应的行和列的数据库记录进行比较。

最后,我们关闭与数据库的连接。尽管对于SQLite来说这并不重要,但关闭与其他数据库的连接通常非常重要——当然,Squish会为我们关闭连接,但我们更喜欢明确我们的意图。

如何直接将测试结果记录到数据库中

绿色测试(Squish)可以输出测试结果为纯文本或XML格式,因此很容易解析并分析结果以及生成报告(例如,请参阅如何进行自动批次测试处理测试结果)。然而,如果需要,我们也可以自己从测试脚本直接记录测试结果。一种方法是利用脚本语言的日志记录功能(如果有的话),例如使用Python的logging模块。另一种方法是直接将结果记录到数据库中——这就是本小节将要介绍的方法。

在我们的示例中,我们将使用一个简单的SQLite 3数据库,该数据库存储在测试套件共享测试数据中的logfile.db3文件中。该数据库有三个字段:id(自增整数)、resultmessage,都是文本字段。我们的测试代码假设数据库已存在(因此,最初为空)。

我们将从查看测试用例的main函数开始,并检查调用Squish的test.log(message)函数的地方是否被替换为调用自定义的DB类实例的log方法,以及类似地调用Squish的Boolean test.compare(value1, value2)Boolean test.verify(condition)函数的地方是否被替换为调用自定义的db对象的compareverify方法。 (注意:对于Tcl,我们不会创建自定义类或对象,只需使用普通函数即可。)

def main():
    startApplication('"' + os.environ["SQUISH_PREFIX"] + '/examples/java/itemviews/ItemViewsSwing.jar"')
    db = None
    try:
        db = DB()
        tableWidgetName = names.item_Views_javax_swing_JTable
        tableWidget = waitForObject(tableWidgetName)
        model = tableWidget.getModel()
        for row in range(model.getRowCount()):
            for column in range(model.getColumnCount()):
                item = model.getValueAt(row, column)
                selected = ""
                if tableWidget.isCellSelected(row, column):
                    selected = " +selected"
                message = "(%d, %d) '%s'%s" % (row, column, item.toString(),
                        selected)
                db.log(message)
            expected = bool(row in (14, 24))
            db.compare(model.getValueAt(row, 0).toString(), str(expected))
        db.verify(model.getRowCount() == 25)
    finally:
        if db is not None:
            db.close()
function main()
{
    startApplication('"' + OS.getenv("SQUISH_PREFIX") + '/examples/java/itemviews/ItemViewsSwing.jar"');
    var db;
    try {
        db = new DB();
        var tableWidgetName = names.itemViewsJavaxSwingJTable;
        var tableWidget = waitForObject(tableWidgetName);
        var model = tableWidget.getModel();
        for (var row = 0; row < model.getRowCount(); ++row) {
            for (var column = 0; column < model.getColumnCount(); ++column) {
                var item = model.getValueAt(row, column);
                var selected = "";
                if (tableWidget.isCellSelected(row, column)) {
                    selected = " +selected";
                }
                var message = "(" + String(row) + ", " + String(column) + ") '" +
                         item.toString() + "'" + selected;
                db.log(message);
            }
            var expected = new Boolean((row == 14 || row == 24) ? true : false);
            db.compare(model.getValueAt(row, 0).toString(), expected.toString());
        }
        db.verify(model.getRowCount() == 25);
    }
    finally {
        if (db)
            db.close();
    }
}
sub main
{
    startApplication("\"$ENV{'SQUISH_PREFIX'}/examples/java/itemviews/ItemViewsSwing.jar\"");
    my $db;
    eval {
        $db = new DB(findFile("testdata", "logfile.db3"));
        my $tableWidgetName = ":Item Views_javax.swing.JTable";
        my $tableWidget = waitForObject($tableWidgetName);
        my $model = $tableWidget->getModel();
        for (my $row = 0; $row < $model->getRowCount(); ++$row) {
            for (my $column = 0; $column < $model->getColumnCount();
                ++$column) {
                my $item = $model->getValueAt($row, $column);
                my $selected = "";
                if ($tableWidget->isCellSelected($row, $column)) {
                    $selected = " +selected";
                }
                $db->log("($row, $column) '$item'$selected");
            }
            my $expected = ($row == 14 || $row == 24) ? "true" : "false";
            $db->compare($model->getValueAt($row, 0), $expected);
        }
        $db->verify($model->getRowCount() == 25);
    };
    if ($@) {
        test::fatal("$@");
    }
    else {
        $db->close;
    }
}
proc main {} {
    startApplication "\"$::env(SQUISH_PREFIX)/examples/java/itemviews/ItemViewsSwing.jar\""
    sqlite3 db [findFile "testdata" "logfile.db3"]
    set tableWidgetName ":Item Views_javax.swing.JTable"
    set tableWidget [waitForObject $tableWidgetName]
    set model [invoke $tableWidget getModel]
    for {set row 0} {$row < [invoke $model getRowCount]} {incr row} {
        for {set column 0} {$column < [invoke $model getColumnCount]} \
            {incr column} {
            set item [invoke $model getValueAt $row $column]
            set selected ""
            if {[invoke $tableWidget isCellSelected $row $column]} {
                set selected " +selected"
            }
            set text [invoke $item toString]
            set message "($row, $column) '$text'$selected"
            db:log db $message
        }
        set expected "false"
        if {$row == 14 || $row == 24} {
            set expected "true"
        }
        set value [invoke [invoke $model getValueAt $row 0] toString]
        db:compare db $value $expected
    }
    db:verify db [expr {[invoke $model getRowCount] == 25}]
    db close
}

main函数与我们在itemviews示例中看到的一个非常相似(请参阅如何测试JList、JTable和JTree小部件(Java—AWT/Swing))。这个函数遍历表格小部件中的每一行以及每一行的每一个单元格。对于每个单元格,我们使用形如"(row, column) text"的字符串记录其内容,对于选中的单元格,可选地在文本末尾添加"+selected" 。表的第一行是复选框——这些复选框的文本为"true"或"false",我们检查每个复选框以确保它们没有被选中(或者在第14行和第24行的情况下,被选中)。最后,我们验证表确实有25行。

DB类的方法(对于Tcl,即为db:*函数)比Squish的内置测试方法更简单、更不复杂,但它们展示了概念证明——当然,您可以创建自己的数据库日志函数,并使其尽可能复杂。

对于DB类,我们首先创建一个实例——我们将很快看到,数据库连接是在构造函数中建立的。然后我们在db对象上调用方法,而不是使用我们通常使用的Squish测试方法。

大多数脚本语言要么没有析构函数,要么析构函数不一定会被调用(或者在JavaScript的情况下,根本就没有析构函数的概念),因此我们使用适当的脚本语言构造来确保如果db对象成功创建,它将在结束时关闭——在这个关闭方法中,将关闭数据库连接。

我们现在可以回顾DB类及其方法(对于Tcl,则为db:*函数)了。然而,正如我们在上一节所述,不同脚本语言的数据库API有很大的差别,因此我们将分别在每个语言的小节中查看这个类的实现——每个小节都是完整的,因此您只需要阅读与您感兴趣的脚本语言相关的小节。

在Python中将结果直接记录到数据库中

import sqlite3

class DB:

    def __init__(self):
        self.db = self.cursor = None
        self.db = sqlite3.connect(findFile("testdata", "logfile.db3"))
        self.cursor = self.db.cursor()

    def log(self, message):
        self.cursor.execute("INSERT INTO log (result, message) "
            "VALUES ('LOG', ?)", (message,))

    def compare(self, first, second):
        if first == second:
            result = "PASS"
        else:
            result = "FAIL"
        self.cursor.execute("INSERT INTO log (result, message) "
            "VALUES (?, 'Comparison')", (result,))

    def verify(self, condition):
        if condition:
            result = "PASS"
        else:
            result = "FAIL"
        self.cursor.execute("INSERT INTO log (result, message) "
            "VALUES (?, 'Verification')", (result,))

    def close(self):
        if self.db is not None:
            self.db.commit()
        if self.cursor is not None:
            self.cursor.close()
        if self.db is not None:
            self.db.close()

当然,我们首先需要导入sqlite3模块。

DB 类假设数据库已存在,并且包含一个名为 log 的表,该表至少包含两个文本字段,即 resultmessage。实际上,在这个例子中,我们用来创建表的 SQLite SQL 语句如下: CREATE TABLE log (id INTEGER PRIMARY KEY, result TEXT, message TEXT)。由于 id 字段是自动增长的,所以我们不需要显式插入该字段的值。

需要注意的一个小点是,当我们调用 cursor.execute 方法(即使用占位符 “?”,按此方式 以及此处使用的方式)时,第二个参数*必须*是元组,即使我们只有单个值要传递,正如这里实现的全部方法。

DB 类显然非常简单,但它展示了我们可以如何创建一个数据库意识对象,我们可以使用它来存储各种测试数据和结果,以便后续处理或报告。

将结果直接记录到 JavaScript 中的数据库

function DB()
{
    var logfile = findFile("testdata", "logfile.db3");
    this.connection = SQL.connect({Driver: "SQLite", Host: "localhost",
        Database: logfile, UserName: "", Password: ""});
}

DB.prototype.log = function(message)
{
    message = message.replace(RegExp("'", "g"), "");
    this.connection.execute("INSERT INTO log (result, message) " +
        "VALUES ('LOG', '" + message + "')");
}

DB.prototype.compare = function(first, second)
{
    var result = first == second ? "PASS" : "FAIL";
    this.connection.execute("INSERT INTO log (result, message) " +
        "VALUES ('" + result + "', 'Comparison')");
}

DB.prototype.verify = function(condition)
{
    var result = condition ? "PASS" : "FAIL";
    this.connection.execute("INSERT INTO log (result, message) " +
        "VALUES ('" + result + "', 'Verification')");
}

DB.prototype.close = function()
{
    this.connection.close();
}

DB 函数是构造函数,我们用它来创建数据库连接。为了向调用 new DB() 返回的对象提供方法,我们创建匿名函数,我们立即将其分配给 DB 类的原型,并使用我们想要调用的名称。

针对 DB.log 方法,我们删除消息中的任何单引号,因为我们创建的 SQL 要执行的完全是字符串,单引号会混淆事物。(另一种选择是将它们转义。)

DB 类假设数据库已存在,并且包含一个名为 log 的表,该表至少包含两个文本字段,即 resultmessage。实际上,在这个例子中,我们用来创建表的 SQLite SQL 语句如下: CREATE TABLE log (id INTEGER PRIMARY KEY, result TEXT, message TEXT)。由于 id 字段是自动增长的,所以我们不需要显式插入该字段的值。

DB 类显然非常简单,但它展示了我们可以如何创建一个数据库意识对象,我们可以使用它来存储各种测试数据和结果,以便后续处理或报告。

将结果直接记录到 Perl 中的数据库

use DBI;
package DB;

sub new {
    my $self = shift;
    my $class = ref($self) || $self;
    my $db3file = shift;
    my $db = DBI->connect("dbi:SQLite:$db3file") ||
        die("Failed to connect: $DBI::errstr");
    $self = { "db" => $db };
    return bless $self, $class;
}

sub log {
    my ($self, $message) = @_;
    my $query = $self->{db}->prepare("INSERT INTO log (result, message) " .
        "VALUES ('LOG', ?)");
    $query->execute($message);
}

sub compare {
    my ($self, $first, $second) = @_;
    my $result = ($first eq $second) ? "PASS" : "FAIL";
    my $query = $self->{db}->prepare("INSERT INTO log (result, message) " .
        "VALUES (?, 'Comparison')");
    $query->execute($result);
}

sub verify {
    my ($self, $condition) = @_;
    my $result = $condition ? "PASS" : "FAIL";
    my $query = $self->{db}->prepare("INSERT INTO log (result, message) " .
        "VALUES (?, 'Verification')");
    $query->execute($result);
}

sub close {
    my $self = shift;
    $self->{db}->disconnect;
}

当然,我们必须首先使用 DBI 模块来提供数据库访问。

DB 类假设数据库已存在,并且包含一个名为 log 的表,该表至少包含两个文本字段,即 resultmessage。实际上,在这个例子中,我们用来创建表的 SQLite SQL 语句如下: CREATE TABLE log (id INTEGER PRIMARY KEY, result TEXT, message TEXT)。由于 id 字段是自动增长的,所以我们不需要显式插入该字段的值。

需要注意的一个小点是,如果我们调用 prepare 函数时使用占位符(即 使用 “?”,正如我们应该做的那样,并且在这里做的那样),我们必须传递在调用 execute 方法时要使用的实际值。

DB 类显然非常简单,但它展示了我们可以如何创建一个数据库意识对象,我们可以使用它来存储各种测试数据和结果,以便后续处理或报告。

将结果直接记录到 Tcl 中的数据库

package require sqlite3

proc db:log {db message} {
    db eval {INSERT INTO log (result, message) VALUES ("LOG", $message)}
}

proc db:compare {db first second} {
    if {$first == $second} {
        set result "PASS"
    } else {
        set result "FAIL"
    }
    db eval {INSERT INTO log (result, message) VALUES \
        ($result, "Comparison")}
}

proc db:verify {db condition} {
    if {$condition} {
        set result "PASS"
    } else {
        set result "FAIL"
    }
    db eval {INSERT INTO log (result, message) VALUES \
        ($result, "Verification")}
}

当然,我们必须首先导入 sqlite3 模块。(有关另一种导入方式,请参阅使用 Tcl 与 GUI 表比较数据库表

db:* 函数都期望将其第一个参数传递为一个 SQLite 3 数据库连接对象。所有三个函数都假设数据库已存在且包含一个名为 log 的表,该表至少包含两个文本字段,即 resultmessage。实际上,在这个例子中,我们用来创建表的 SQLite SQL 语句如下: CREATE TABLE log (id INTEGER PRIMARY KEY, result TEXT, message TEXT)。由于 id 字段是自动增长的,所以我们不需要显式插入该字段的值。

显然,这些函数非常简单,但它们展示了我们如何创建可以用于存储各种测试数据和结果的数据库意识函数的基本原理,以便进行后续处理或报告。

©2024 The Qt Company Ltd. 本文档贡献的版权属于其各自的拥有者。
本文档所提供的文档受 自由软件基金会发布的 GNU 自由文档许可证(版本 1.3) 的条款约束。
Qt 和各自的标志是芬兰 Qt 公司及其在全世界其他国家的商标。所有其他商标均为各自所有者的财产。