在我们的项目中,我们使用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一起使用。
使用此技巧,您可以将单个测试xml报告收集到临时缓冲区/文件中;全部来自单个测试二进制文件。让我们使用QProcess从一个二进制文件中收集单独的测试输出;测试使用修改后的参数调用自己。首先,我们引入了一个特殊的命令行参数,它适当地利用了子测试——所有子测试仍然在您的测试可执行文件中。为了方便起见,我们使用了接受QStringList的重载qExec函数。然后我们可以插入/移除我们的"-子测试";辩论更容易。
// 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的功能为您收集stdout。只需将这些行附加到主行即可。我们的想法是,如果没有明确的测试请求,再次调用我们的可执行文件,但使用我们的特殊参数:
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版本能让这件事变得更容易。
我使用了这个肮脏的解决方法(与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中,我添加了构建操作"execute shell":/path_to_MyAppTests-xml
并添加了构建后操作"发布xUnit测试结果报告"(QTestlib)。QTestlib模式:MyAppTests*.xml
由于我还不能在这里发表评论,我将在muenalan的回答后面添加它。要使其工作(至少在Qt5中),需要应用一些修复程序:
-
callSubtestAndStoreStdout有3个错误。首先,在推送新的arg之前,必须从前面弹出第一个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);
-
main也有一些(明显的)错误。主要是在if语句中:
if ((-1 != pos) && (pos + 1 < args.length()))
因为最初的一个永远不会开火。
不管怎样,谢谢你的解决方案,它解决了我头疼的问题:)
在我看来,尝试构建一个可执行文件是个坏主意:如果你的一个测试崩溃,其他测试就不会再执行了。。。
运行具有多个测试用例的套件的另一种方法:
- 在顶层创建一个细分项目
- 为每个测试用例添加一个带有自己的.pro的子文件夹,并将其添加到subdirs项目中
- 从顶层文件夹生成项目
- 在顶层生成文件上运行
make check
。这将调用您的所有测试用例。您也可以传递参数,例如在MSVC环境中使用nmake -k check TESTARGS="-o result.xml,xml -v2 -maxwarnings 0"
。如果一个测试失败,-k开关有助于继续 - 以xunit-Jenkins插件为例,它允许
my_build*result.xml
这样的模式来搜索xml文件,这样您就可以解析所有生成的文件,而无需合并到一个文件中