Chapter 2: Data Driven Testing

这一章, 我们讨论如何使用不同的测试数据运行多次同一个测试函数.

如下所示, 我们将在测试程序中添加多个测试数据:


  QCOMPARE(QString("hello").toUpper(), QString("HELLO"));
  QCOMPARE(QString("Hello").toUpper(), QString("HELLO"));
  QCOMPARE(QString("HellO").toUpper(), QString("HELLO"));
  QCOMPARE(QString("HELLO").toUpper(), QString("HELLO"));

为防止测试函数被重复代码弄得一团糟, Qt Test支持将测试数据添加到测试函数中. 我们只需在测试类中添加一个私有槽函数:


  class TestQString: public QObject
  {
      Q_OBJECT

  private slots:
      void toUpper_data();
      void toUpper();
  };

编写数据函数

一个与测试函数相关的数据函数应该与测试函数有相同的名字, 并加上后缀_data. 我们的数据函数如下所示:


  void TestQString::toUpper_data()
  {
      QTest::addColumn<QString>("string");
      QTest::addColumn<QString>("result");

      QTest::newRow("all lower") << "hello" << "HELLO";
      QTest::newRow("mixed")     << "Hello" << "HELLO";
      QTest::newRow("all upper") << "HELLO" << "HELLO";
  }

首先, 我们调用QTest::addColumn()函数在测试数据表单中定义两个元素: 测试字符串和QString::toUpper()函数的期望运行结果.

然后, 我们调用QTest::newRow()函数增加一些数据. 在表单中, 每组数据是一个独立的行.

QTest::newRow()需要一个参数: 数据关联的名称, 在测试日志中标识数据. 然后, 我们将数据集流式地传输到表行中. 先是一个任意字符串, 然后是对这个字符串经QString::toUpper()运算后的预期结果.

你可以将测试数据视作一个二维表格. 在示例中, 它有两列(stringresult)和三行. 在每一行增加一个类似索引的名称:

indexnamestringresult
0all lower"hello"HELLO
1mixed"Hello"HELLO
2all upper"HELLO"HELLO

当数据以流式传输到行中时, 每个数据都被断言为与它所提供值的列的类型相匹配. 如果任何断言失败, 测试会中止.

重新实现测试函数

现在, 测试函数以如下方式重新实现:


  void TestQString::toUpper()
  {
      QFETCH(QString, string);
      QFETCH(QString, result);

      QCOMPARE(string.toUpper(), result);
  }

TestQString::toUpper()函数将执行三次, TestQString::toUpper()执行的数据是我们在QTestQString::toUpper_data()函数中关联的数据, 每行数据执行一次.

首先, 我们使用QFETCH()宏获取数据集中的两个元素. QFETCH()需要两个参数: 元素类型和元素名称. 然后, 我们使用QCOMPARE()宏执行测试.

这种方式使得向测试添加新数据变得非常容易, 而无需修改测试本身.

同样, 为了使我们的测试用例成为独立的可执行文件, 需要以下两行:


  QTEST_MAIN(TestQString)
  #include "testqstring.moc"

和以前一样, QTEST_MAIN() 宏扩展为一个简单的main()方法, 这个方法运行所有测试函数, 并且由于我们的测试类的声明和实现都在 .cpp 文件中, 因此我们还需要包含生成的 moc 文件以使 Qt 的自省工作.

Files: