先日、Windows プラットフォーム (Visual Studio 2010 Premium を使用) での C++ のテスト駆動開発について知る必要があると判断しました。
ブーストのユニットテストフレームワークを試すことに決める前に、私は周りを見回しました。私は、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
スレッドの COM アパートメントを初期化していないため、EncoderAvailable テストが失敗すると予想していました。自動テストを使用できないと仮定し、代わりに自動テストをテストに置き換える必要があります。メイン関数で自分自身を手動で定義し、メイン関数で 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() 呼び出しを行う正しい場所を誰か教えてもらえますか - テストごとに 1 回だけ行うべきではないと思います - スレッドごとに 1 回だけ行う必要があります...
exe プロジェクトのテストに関しては、別の main.cpp (testmain.cpp など) を指定し、実際の main.cpp をビルドから除外してコードにアクセスできると思います。誰かがそれに対するよりエレガントな解決策を知っているなら、私はそれについて聞きたいと思っています...