CoInitializeEx 用于 boost::test::unit_test
有一天,我决定需要了解 Windows 平台上的 C++ 测试驱动开发(使用 Visual Studio 2010 Premium)。
在决定尝试 boost 的单元测试框架之前,我环顾四周。我应该说我选择了 boostpro.com 的版本(如果我没记错的话,当前版本是 1.44)。它有一个静态库的构建,所以我在测试中没有使用 DLL。
Boost 的单元测试文档讨论了将代码与测试套件分离,这似乎很合理。但是,您必须处理从现在单独的测试套件项目引用代码的问题。
所以我有一个想要测试的库项目(但我仍然不确定如何编写可以引用 .exe 项目的测试...)
所以我在我的解决方案中创建了一个单独的项目,称为单元测试。我添加了以下代码:
#include "stdafx.h"
#define BOOST_TEST_MODULE Crash
#include <boost/test/unit_test.hpp>
#include "LameEncoder.h"
BOOST_AUTO_TEST_SUITE(CrashTestSuite)
BOOST_AUTO_TEST_CASE(EncoderAvailable)
{
using namespace Crash::SystemDevices::Audio::Compressors::LameEncoder;
HRESULT hr = S_OK;
CComPtr <IBaseFilter> spEncoder;
hr = spEncoder.CoCreateInstance( CLSID_LAMEDShowFilter );
if( spEncoder.p )
spEncoder.Release();
BOOST_CHECK_EQUAL( hr, S_OK );
}
BOOST_AUTO_TEST_CASE(ProfilesGenerated)
{
using namespace Crash::SystemDevices::Audio::Compressors::LameEncoder;
BOOST_CHECK_EQUAL ( EncoderProfiles.size(), 6 );
}
BOOST_AUTO_TEST_SUITE_END()
我静态链接到我的“崩溃”库项目输出,然后添加了以下构建后事件以获取构建后报告:构建
"$(TargetDir)\$(TargetName).exe" --result_code=no --report_level=short
后输出如下所示:
1>------ Build started: Project: UnitTests, Configuration: Debug Win32 ------
1> UnitTests.cpp
1> UnitTests.vcxproj -> F:\Projects\Crash\trunk\Debug\UnitTests.exe
1> Running 2 test cases...
1> f:/projects/crash/trunk/unittests/unittests.cpp(19): error in "EncoderAvailable": check hr == ((HRESULT)0L) failed [-2147221008 != 0]
1>
1> Test suite "Crash" failed with:
1> 1 assertion out of 2 passed
1> 1 assertion out of 2 failed
1> 1 test case out of 2 passed
1> 1 test case out of 2 failed
我预计 EncoderAvailable 测试会失败,因为我还没有为线程初始化 COM 单元。我假设我不能使用自动测试,而是需要用我在主函数中手动定义自己的测试替换自动测试,并在主函数中执行 CoInitializeEx() 调用。
我读过 这里,你可以定义入口点并注册你自己的函数,所以我试了一下:
#include "stdafx.h"
#include <boost/test/unit_test.hpp>
using namespace boost::unit_test;
#include "LameEncoderTests.h"
test_suite*
init_unit_test_suite( int argc, char* argv[] )
{
CoInitializeEx(NULL, COINIT_MULTITHREADED);
framework::master_test_suite().
add( BOOST_TEST_CASE( &LameEncoderAvailable ) );
framework::master_test_suite().
add( BOOST_TEST_CASE( &LameEncoderProfilesGenerated ) );
CoUninitialize();
return 0;
}
这是构建输出:
1>------ Build started: Project: UnitTests, Configuration: Debug Win32 ------
1> UnitTests.cpp
1> UnitTests.vcxproj -> F:\Projects\Crash\trunk\Debug\UnitTests.exe
1> Running 2 test cases...
1> f:/projects/crash/trunk/unittests/lameencodertests.h(17): error in "LameEncoderAvailable": check hr == ((HRESULT)0L) failed [-2147221008 != 0]
1>
1> Test suite "Master Test Suite" failed with:
1> 1 assertion out of 2 passed
1> 1 assertion out of 2 failed
1> 1 test case out of 2 passed
1> 1 test case out of 2 failed
1>
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
测试失败在第一个测试 LameEncoderAvailable 上失败,其中是以下简单的函数:
void LameEncoderAvailable()
{
using namespace Crash::SystemDevices::Audio::Compressors::LameEncoder;
HRESULT hr = S_OK;
CComPtr<IBaseFilter> spEncoder;
hr = spEncoder.CoCreateInstance( CLSID_LAMEDShowFilter );
if( spEncoder.p )
spEncoder.Release();
BOOST_CHECK_EQUAL( hr, S_OK );
}
任何人都可以告诉我在哪里进行 CoInitializeEx() 调用的正确位置 - 我认为我不应该每次测试都这样做一次 - 每个线程应该只执行一次...
至于测试exe 项目,我想您可以指定一个单独的 main.cpp (testmain.cpp 或其他东西)并从构建中排除真正的 main.cpp 以访问您的代码。如果有人知道一个更优雅的解决方案,我很想听听......
The other day, I decided that I needed to know about test driven development for C++ on the Windows platform (using Visual Studio 2010 Premium).
I had a look around before settling on trying out boost's unit test framework. I should say that I opted for boostpro.com's release (current is 1.44 if I recall correctly). This has a build of the static library, so I don't use the DLL in my tests.
Boost's unit test documentation talks about seperating your code from your test suite, which seems reasonable. But then you must deal with the problem of referencing your code from your now seperate test suite project.
So I have a library project that I want to test (but I'm still not sure how I'd write tests that can reference an .exe project...)
So I created a seperate project in my solution called unit tests. I added the following code:
#include "stdafx.h"
#define BOOST_TEST_MODULE Crash
#include <boost/test/unit_test.hpp>
#include "LameEncoder.h"
BOOST_AUTO_TEST_SUITE(CrashTestSuite)
BOOST_AUTO_TEST_CASE(EncoderAvailable)
{
using namespace Crash::SystemDevices::Audio::Compressors::LameEncoder;
HRESULT hr = S_OK;
CComPtr <IBaseFilter> spEncoder;
hr = spEncoder.CoCreateInstance( CLSID_LAMEDShowFilter );
if( spEncoder.p )
spEncoder.Release();
BOOST_CHECK_EQUAL( hr, S_OK );
}
BOOST_AUTO_TEST_CASE(ProfilesGenerated)
{
using namespace Crash::SystemDevices::Audio::Compressors::LameEncoder;
BOOST_CHECK_EQUAL ( EncoderProfiles.size(), 6 );
}
BOOST_AUTO_TEST_SUITE_END()
I statically link to my "crash" library project output, then I added the following post-build event to get a report post-build:
"$(TargetDir)\$(TargetName).exe" --result_code=no --report_level=short
The post build output looks like this:
1>------ Build started: Project: UnitTests, Configuration: Debug Win32 ------
1> UnitTests.cpp
1> UnitTests.vcxproj -> F:\Projects\Crash\trunk\Debug\UnitTests.exe
1> Running 2 test cases...
1> f:/projects/crash/trunk/unittests/unittests.cpp(19): error in "EncoderAvailable": check hr == ((HRESULT)0L) failed [-2147221008 != 0]
1>
1> Test suite "Crash" failed with:
1> 1 assertion out of 2 passed
1> 1 assertion out of 2 failed
1> 1 test case out of 2 passed
1> 1 test case out of 2 failed
I expected the EncoderAvailable test to fail, since I haven't initialized a COM apartment for the thread. I'd assume that I can't use auto tests, and instead I need to replace the auto tests with tests I manually define myself in a main function, and do my CoInitializeEx() calls in the main function.
I've read here that you can define the entry point and register your own functions, so I gave this a go:
#include "stdafx.h"
#include <boost/test/unit_test.hpp>
using namespace boost::unit_test;
#include "LameEncoderTests.h"
test_suite*
init_unit_test_suite( int argc, char* argv[] )
{
CoInitializeEx(NULL, COINIT_MULTITHREADED);
framework::master_test_suite().
add( BOOST_TEST_CASE( &LameEncoderAvailable ) );
framework::master_test_suite().
add( BOOST_TEST_CASE( &LameEncoderProfilesGenerated ) );
CoUninitialize();
return 0;
}
Here's the build ouptut:
1>------ Build started: Project: UnitTests, Configuration: Debug Win32 ------
1> UnitTests.cpp
1> UnitTests.vcxproj -> F:\Projects\Crash\trunk\Debug\UnitTests.exe
1> Running 2 test cases...
1> f:/projects/crash/trunk/unittests/lameencodertests.h(17): error in "LameEncoderAvailable": check hr == ((HRESULT)0L) failed [-2147221008 != 0]
1>
1> Test suite "Master Test Suite" failed with:
1> 1 assertion out of 2 passed
1> 1 assertion out of 2 failed
1> 1 test case out of 2 passed
1> 1 test case out of 2 failed
1>
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
That test failure fails on the first test LameEncoderAvailable, which is the following simple function:
void LameEncoderAvailable()
{
using namespace Crash::SystemDevices::Audio::Compressors::LameEncoder;
HRESULT hr = S_OK;
CComPtr<IBaseFilter> spEncoder;
hr = spEncoder.CoCreateInstance( CLSID_LAMEDShowFilter );
if( spEncoder.p )
spEncoder.Release();
BOOST_CHECK_EQUAL( hr, S_OK );
}
Can anyone tell me where the correct place to make the CoInitializeEx() call - I don't think I should be doing so once per test - it should only be done once per thread...
As for testing exe projects, I guess you could specify a separate main.cpp (testmain.cpp or something) and exclude your real main.cpp from the build to access your code. If anyone knows of a more elegant solution to that one, I'd be keen to hear about it...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
使用 全局夹具。夹具是为每个测试设置初始化/关闭代码的好方法。全局夹具允许您为整个测试套件定义初始化/关闭代码。
Use a Global Fixture. Fixtures are a great way to set up initialization/shutdown code for each test. A global fixture lets you define initialization/shutdown code for your whole test suite.
未经测试,但是在构造函数中调用 CoInitializeEx() 的全局变量会有帮助吗?
Untested, but would a global variable that invokes CoInitializeEx() in the constructor help?
为什么不为每个测试都进行 CoInitialize、CoUnitialize 呢?
编辑:
假设测试确实是同时执行的。一种方法是使用一个特定于线程的容器来保存 RAII CoInitialize/CoUnitialize 对象。
Why not do CoInitialize, CoUnitialize for each test?
EDIT:
Assuming that the tests are indeed executed concurrently. One way to do it is to have a thread_specific container which holds a RAII CoInitialize/CoUnitialize object.
您不需要使用 init_unit_test_suite,因为此函数是在任何测试运行之前执行的。
您确实想使用全局夹具。调用 CoInitializeEx(NULL, COINIT_MULTITHREADED);
在构造函数和 CoUnInitializeEx() 中;在析构函数中
You do NOT need to use init_unit_test_suite, since this function is executed before any of the tests run.
You do want to use Global fixture. Call CoInitializeEx(NULL, COINIT_MULTITHREADED);
in constructor and CoUnInitializeEx(); in destructor