【问题标题】:How to compose all QtTestLib unit tests' results in a single file while using a single test project?如何在使用单个测试项目时将所有 QtTestLib 单元测试的结果组合在一个文件中?
【发布时间】:2012-04-14 05:29:54
【问题描述】:

在我们的项目中,我们使用 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 一起使用。

【问题讨论】:

    标签: c++ qt qtestlib qttest


    【解决方案1】:

    通过这个技巧,您可以将单独的测试 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 版本可以让这更容易。

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

        【解决方案3】:

        由于我还不能在这里发表评论,因此我将在此处发布以补充 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()))
          

        因为原版永远不会开火。

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

        【讨论】:

        • 是 Qt4.8,未经过全面测试。想说明原理。谢谢,已对上述答案进行了修复。
        【解决方案4】:

        我使用过这种肮脏的解决方法(与 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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-09-08
          • 2011-02-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多