6

在我们的项目中,我们使用 QtTestLib 进行单元测试。原因是整个项目已经尽可能地使用 Qt 并且它是一个 GUI 应用程序,所以我们希望能够测试 GUI 界面。

我们的项目是由 MSVC 编译的,所以我们不希望每个测试都有一个单独的项目文件,因为它会使解决方案变得混乱。因此,我们为所有测试创建了一个项目。所有测试都应该在 CIS(持续集成)上自动化,因此我们尝试使用一些 XSLT 转换通过 XML 格式的输出文件将测试插入 Hudson。

但似乎测试的输出存在问题。如果您对所有测试使用单个 main() 并且仅将 cmd 行参数传输到每个测试:

#include "MyFirstTest.h"
#include "MySecondTest.h"

int main(int argc, char **argv)
{
  int result = 0;
  MyFirstTest test1;
  result |= QTest::qExec(&test1, argc, argv);
  MySecondTest test2;
  result |= QTest::qExec(&test2, argc, argv);
  return result;
}

然后你会得到一个被多次重写的结果文件。因此,如果您想使用输出文件(例如 xml)使其自动化,您将只获得其中的最后一个结果。所有其他将被覆盖。

我们已经尝试过这种方法,它无法让您使用像 Hudson 这样的持续集成系统。所以我的问题是:有没有机会将结果附加到一个输出文件中?当然,我们可以使用一些变通方法,例如通过 QTest::qExec() 运行每个测试并使用修改后的参数将结果写入单独的文件中,但这似乎不是最好的方法。理想情况下,我希望有一个结果文件来与 CIS 一起使用。

4

4 回答 4

3

通过这个技巧,您可以将单独的测试 xml 报告收集到临时缓冲区/文件中;全部来自一个测试二进制文件。让我们使用 QProcess 从一个二进制文件中收集单独的测试输出;测试使用修改后的参数调用自身。首先,我们引入了一个特殊的命令行参数,它可以正确地利用子测试——所有这些都仍然在您的测试可执行文件中。为方便起见,我们使用接受 QStringList 的重载 qExec 函数。然后我们可以更轻松地插入/删除我们的“-subtest”参数。

// Source code of "Test"

int
main( int argc, char** argv )
{
  int result = 0;

  // The trick is to remove that argument before qExec can see it; As qExec could be
  // picky about an unknown argument, we have to filter the helper 
  // argument (below called -subtest) from argc/argc; 

  QStringList args;

  for( int i=0; i < argc; i++ )
  {
     args << argv[i];
  }

  // Only call tests when -subtest argument is given; that will usually
  // only happen through callSubtestAndStoreStdout

  // find and filter our -subtest argument

  size_t pos = args.indexOf( "-subtest" );

  QString subtestName;

  if( (-1 != pos) && (pos + 1 < args.length()) )
  {
    subtestName = args.at( pos+1 );

    // remove our special arg, as qExec likely confuses them with test methods

    args.removeAt( pos );
    args.removeAt( pos );

    if( subtestName == "test1" )
    {
      MyFirstTest test1;
      result |= QTest::qExec(&test1, args);
    }

    if( subtestName == "test2" )
    {
      MySecondTest test2;
      result |= QTest::qExec(&test2, args);
    }

    return result;
}

然后,在您的脚本/命令行调用中:

./Test -subtest test1 -xml ... >test1.xml
./Test -subtest test2 -xml ... >test2.xml

你在这里 - 我们有办法分离测试输出。现在我们可以继续使用 QProcess 的能力为您收集标准输出。只需将这些行附加到您的主目录。这个想法是再次调用我们的可执行文件,如果没有要求明确的测试,但使用我们的特殊参数:

bool
callSubtestAndStoreStdout(const String& subtestId, const String& fileNameTestXml, QStringList args)
{
   QProcess proc;

   args.pop_front();

   args.push_front( subtestId );
   args.push_front( "-subtest" );

   proc.setStandardOutputFile( fileNameTestXml );

   proc.start( "./Test", args );

   return proc.waitForFinished( 30000 ); // int msecs
}

int 
main( int argc, char** argv )
{
   .. copy code from main in box above..

   callSubtestAndStoreStdout("test1", "test1.xml", args);
   callSubtestAndStoreStdout("test2", "test2.xml", args);

   // ie. insert your code here to join the xml files to a single report

   return result;
}

然后在您的脚本/命令行调用中:

./Test -xml           # will generate test1.xml, test2.xml

事实上,希望未来的 QTestLib 版本可以让这更容易做到。

于 2012-04-15T20:20:24.933 回答
3

我使用了这种肮脏的解决方法(与 Jenkins 一起使用):

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    int result = 0;
    freopen("MyAppTests_Test1.xml", "w", stdout);
    result |= QTest::qExec(new Test1, argc, argv);
    freopen("MyAppTests_Test2.xml",  "w", stdout);
    result |= QTest::qExec(new Test2, argc, argv);
    return result;
}

然后在 Jenkins 我添加了构建操作“执行 shell”:./path_to_MyAppTests -xml

并添加了构建后操作“发布 xUnit 测试结果报告”(QTestlib)。QTestlib 模式:MyAppTests*.xml

于 2014-03-21T09:12:36.990 回答
2

由于我还不能在这里发表评论,因此我将在此处发布,以补充 muenalan 的答案。几乎没有必须应用的修复程序才能工作(至少在 Qt5 中):

  1. callSubtestAndStoreStdout有 3 个错误。首先,第一个 arg 必须从前面弹出(这是 arg 0),然后再推送新的。其次,您必须在开始该过程之前重定向输出。第三,它必须返回一些值;)

    QProcess proc;
    args.pop_front();
    args.push_front(subtestId);
    args.push_front("-subtest");
    
    proc.setStandardOutputFile(fileNameTestXml);
    proc.start("sportSystemTest.exe", args);
    return proc.waitForFinished(30000);
    
  2. main 也有一些(明显的)错误。主要的是在if语句中:

    if ((-1 != pos) && (pos + 1 < args.length()))
    

因为原来的永远不会开火。

无论如何,感谢您的解决方案,它解决了我的头疼问题:)

于 2015-11-03T12:16:23.527 回答
2

在我看来,在这里尝试构建单个可执行文件是一个坏主意:如果您的一个测试崩溃,其他测试将不再执行......

运行具有多个测试用例的套件的另一种方法:

  • 在顶层创建一个子目录项目。
  • 为每个测试用例添加一个带有自己的 .pro 的子文件夹,并将其添加到 subdirs 项目中。
  • 从顶层文件夹构建项目
  • make check在顶层 makefile 上运行。这将调用您所有的测试用例。您还可以传递参数,例如nmake -k check TESTARGS="-o result.xml,xml -v2 -maxwarnings 0"与您的 MSVC 环境一起使用。如果一个测试失败,-k 开关有助于继续。
  • 以 xunit Jenkins 插件为例,它允许像my_build\*\result.xml搜索您的 xml 文件这样的模式,这样您就可以解析所有生成的文件而无需合并到单个文件中。
于 2018-02-09T15:47:01.750 回答