Ich bin mit der Boost::Test-Bibliothek für Unit-Tests, und ich habe in der Regel hacking up meine eigenen Mocking-Lösungen, die etwas wie dieses aussehen:
//In header for clients
struct RealFindFirstFile
{
static HANDLE FindFirst(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData) {
return FindFirstFile(lpFileName, lpFindFileData);
};
};
template <typename FirstFile_T = RealFindFirstFile>
class DirectoryIterator {
//.. Implementation
}
//In unit tests (cpp)
#define THE_ANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING 42
struct FakeFindFirstFile
{
static HANDLE FindFirst(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData) {
return THE_ANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING;
};
};
BOOST_AUTO_TEST_CASE( MyTest )
{
DirectoryIterator<FakeFindFirstFile> LookMaImMocked;
//Test
}
Das hat mich frustriert, denn es erfordert, dass ich fast alles als Vorlage implementiere, und es ist eine Menge Standardcode, um das zu erreichen, wonach ich suche.
Gibt es eine gute Methode des Mocking up Code mit Boost::Test über meine Ad-hoc-Methode?
Ich habe gesehen, mehrere Leute empfehlen Google Mock, aber es erfordert eine Menge von hässlichen Hacks, wenn Ihre Funktionen sind nicht virtual
was ich gerne vermeiden möchte.
Oh: Eine letzte Sache. Ich brauche keine Behauptungen, dass ein bestimmtes Stück Code aufgerufen wurde. Ich muss lediglich in der Lage sein, Daten zu injizieren, die normalerweise von Windows-API-Funktionen zurückgegeben werden.
EDIT: Hier ist ein Beispiel-Klassensatz und die Tests, die ich für ihn habe:
Die zu prüfenden Klassen:
#include <list>
#include <string>
#include <boost/noncopyable.hpp>
#include <boost/make_shared.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <Windows.h>
#include <Shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
#include "../Exception.hpp"
namespace WindowsAPI { namespace FileSystem {
//For unit testing
struct RealFindXFileFunctions
{
HANDLE FindFirst(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData) {
return FindFirstFile(lpFileName, lpFindFileData);
};
BOOL FindNext(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData) {
return FindNextFile(hFindFile, lpFindFileData);
};
BOOL Close(HANDLE hFindFile) {
return FindClose(hFindFile);
};
};
class Win32FindData {
WIN32_FIND_DATA internalData;
std::wstring rootPath;
public:
Win32FindData(const std::wstring& root, const WIN32_FIND_DATA& data) :
rootPath(root), internalData(data) {};
DWORD GetAttributes() const {
return internalData.dwFileAttributes;
};
bool IsDirectory() const {
return (internalData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
};
bool IsFile() const {
return !IsDirectory();
};
unsigned __int64 GetSize() const {
ULARGE_INTEGER intValue;
intValue.LowPart = internalData.nFileSizeLow;
intValue.HighPart = internalData.nFileSizeHigh;
return intValue.QuadPart;
};
std::wstring GetFolderPath() const {
return rootPath;
};
std::wstring GetFileName() const {
return internalData.cFileName;
};
std::wstring GetFullFileName() const {
return rootPath + L"\\" + internalData.cFileName;
};
std::wstring GetShortFileName() const {
return internalData.cAlternateFileName;
};
FILETIME GetCreationTime() const {
return internalData.ftCreationTime;
};
FILETIME GetLastAccessTime() const {
return internalData.ftLastAccessTime;
};
FILETIME GetLastWriteTime() const {
return internalData.ftLastWriteTime;
};
};
template <typename FindXFileFunctions_T>
class BasicEnumerationMethod : public boost::noncopyable {
protected:
WIN32_FIND_DATAW currentData;
HANDLE hFind;
std::wstring currentDirectory;
FindXFileFunctions_T FindFileFunctions;
BasicEnumerationMethod(FindXFileFunctions_T functor) :
hFind(INVALID_HANDLE_VALUE),
FindFileFunctions(functor) {};
void IncrementCurrentDirectory() {
if (hFind == INVALID_HANDLE_VALUE) return;
BOOL success =
FindFileFunctions.FindNext(hFind, ¤tData);
if (success)
return;
DWORD error = GetLastError();
if (error == ERROR_NO_MORE_FILES) {
FindFileFunctions.Close(hFind);
hFind = INVALID_HANDLE_VALUE;
} else {
WindowsApiException::Throw(error);
}
};
virtual ~BasicEnumerationMethod() {
if (hFind != INVALID_HANDLE_VALUE)
FindFileFunctions.Close(hFind);
};
public:
bool equal(const BasicEnumerationMethod<FindXFileFunctions_T>& other) const {
if (this == &other)
return true;
return hFind == other.hFind;
};
Win32FindData dereference() {
return Win32FindData(currentDirectory, currentData);
};
};
template <typename FindXFileFunctions_T>
class BasicNonRecursiveEnumeration : public BasicEnumerationMethod<FindXFileFunctions_T>
{
public:
BasicNonRecursiveEnumeration(FindXFileFunctions_T functor = FindXFileFunctions_T())
: BasicEnumerationMethod<FindXFileFunctions_T>(functor) {};
BasicNonRecursiveEnumeration(const std::wstring& pathSpec,
FindXFileFunctions_T functor = FindXFileFunctions_T())
: BasicEnumerationMethod<FindXFileFunctions_T>(functor) {
std::wstring::const_iterator lastSlash =
std::find(pathSpec.rbegin(), pathSpec.rend(), L'\\').base();
if (lastSlash != pathSpec.begin())
currentDirectory.assign(pathSpec.begin(), lastSlash-1);
hFind = FindFileFunctions.FindFirst(pathSpec.c_str(), ¤tData);
if (hFind == INVALID_HANDLE_VALUE
&& GetLastError() != ERROR_PATH_NOT_FOUND
&& GetLastError() != ERROR_FILE_NOT_FOUND)
WindowsApiException::ThrowFromLastError();
while (hFind != INVALID_HANDLE_VALUE && (!wcscmp(currentData.cFileName, L".") ||
!wcscmp(currentData.cFileName, L".."))) {
IncrementCurrentDirectory();
}
};
void increment() {
IncrementCurrentDirectory();
};
};
typedef BasicNonRecursiveEnumeration<RealFindXFileFunctions> NonRecursiveEnumeration;
template <typename FindXFileFunctions_T>
class BasicRecursiveEnumeration : public BasicEnumerationMethod<FindXFileFunctions_T>
{
//Implementation ommitted
};
typedef BasicRecursiveEnumeration<RealFindXFileFunctions> RecursiveEnumeration;
struct AllResults
{
bool operator()(const Win32FindData&) {
return true;
};
};
struct FilesOnly
{
bool operator()(const Win32FindData& arg) {
return arg.IsFile();
};
};
template <typename Filter_T = AllResults, typename Recurse_T = NonRecursiveEnumeration>
class DirectoryIterator :
public boost::iterator_facade<
DirectoryIterator<Filter_T, Recurse_T>,
Win32FindData,
std::input_iterator_tag,
Win32FindData
>
{
friend class boost::iterator_core_access;
boost::shared_ptr<Recurse_T> impl;
Filter_T filter;
void increment() {
do {
impl->increment();
} while (! filter(impl->dereference()));
};
bool equal(const DirectoryIterator& other) const {
return impl->equal(*other.impl);
};
Win32FindData dereference() const {
return impl->dereference();
};
public:
DirectoryIterator(Filter_T functor = Filter_T()) :
impl(boost::make_shared<Recurse_T>()),
filter(functor) {
};
explicit DirectoryIterator(const std::wstring& pathSpec, Filter_T functor = Filter_T()) :
impl(boost::make_shared<Recurse_T>(pathSpec)),
filter(functor) {
};
};
}}
Tests für diese Klasse:
#include <queue>
#include "../WideCharacterOutput.hpp"
#include <boost/test/unit_test.hpp>
#include "../../WindowsAPI++/FileSystem/Enumerator.hpp"
using namespace WindowsAPI::FileSystem;
struct SimpleFakeFindXFileFunctions
{
static std::deque<WIN32_FIND_DATAW> fakeData;
static std::wstring insertedFileName;
HANDLE FindFirst(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData) {
insertedFileName.assign(lpFileName);
if (fakeData.empty()) {
SetLastError(ERROR_PATH_NOT_FOUND);
return INVALID_HANDLE_VALUE;
}
*lpFindFileData = fakeData.front();
fakeData.pop_front();
return reinterpret_cast<HANDLE>(42);
};
BOOL FindNext(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData) {
BOOST_CHECK_EQUAL(reinterpret_cast<HANDLE>(42), hFindFile);
if (fakeData.empty()) {
SetLastError(ERROR_NO_MORE_FILES);
return 0;
}
*lpFindFileData = fakeData.front();
fakeData.pop_front();
return 1;
};
BOOL Close(HANDLE hFindFile) {
BOOST_CHECK_EQUAL(reinterpret_cast<HANDLE>(42), hFindFile);
return 1;
};
};
std::deque<WIN32_FIND_DATAW> SimpleFakeFindXFileFunctions::fakeData;
std::wstring SimpleFakeFindXFileFunctions::insertedFileName;
struct ErroneousFindXFileFunctions
{
virtual HANDLE FindFirst(LPCWSTR, LPWIN32_FIND_DATAW) {
return reinterpret_cast<HANDLE>(42);
};
virtual BOOL FindNext(HANDLE hFindFile, LPWIN32_FIND_DATAW) {
BOOST_CHECK_EQUAL(reinterpret_cast<HANDLE>(42), hFindFile);
return 1;
};
virtual BOOL Close(HANDLE hFindFile) {
BOOST_CHECK_EQUAL(reinterpret_cast<HANDLE>(42), hFindFile);
return 1;
};
};
struct ErroneousFindXFileFunctionFirst : public ErroneousFindXFileFunctions
{
HANDLE FindFirst(LPCWSTR, LPWIN32_FIND_DATAW) {
SetLastError(ERROR_ACCESS_DENIED);
return INVALID_HANDLE_VALUE;
};
};
struct ErroneousFindXFileFunctionNext : public ErroneousFindXFileFunctions
{
BOOL FindNext(HANDLE hFindFile, LPWIN32_FIND_DATAW) {
BOOST_CHECK_EQUAL(reinterpret_cast<HANDLE>(42), hFindFile);
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
};
};
struct DirectoryIteratorTestsFixture
{
typedef SimpleFakeFindXFileFunctions fakeFunctor;
DirectoryIteratorTestsFixture() {
WIN32_FIND_DATAW test;
wcscpy_s(test.cFileName, L".");
wcscpy_s(test.cAlternateFileName, L".");
test.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
GetSystemTimeAsFileTime(&test.ftCreationTime);
test.ftLastWriteTime = test.ftCreationTime;
test.ftLastAccessTime = test.ftCreationTime;
test.nFileSizeHigh = 0;
test.nFileSizeLow = 0;
fakeFunctor::fakeData.push_back(test);
wcscpy_s(test.cFileName, L"..");
wcscpy_s(test.cAlternateFileName, L"..");
fakeFunctor::fakeData.push_back(test);
wcscpy_s(test.cFileName, L"File.txt");
wcscpy_s(test.cAlternateFileName, L"FILE.TXT");
test.nFileSizeLow = 1024;
test.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
fakeFunctor::fakeData.push_back(test);
wcscpy_s(test.cFileName, L"System32");
wcscpy_s(test.cAlternateFileName, L"SYSTEM32");
test.nFileSizeLow = 0;
test.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
fakeFunctor::fakeData.push_back(test);
};
~DirectoryIteratorTestsFixture() {
fakeFunctor::fakeData.clear();
};
};
BOOST_FIXTURE_TEST_SUITE( DirectoryIteratorTests, DirectoryIteratorTestsFixture )
BOOST_AUTO_TEST_CASE( BasicEnumeration )
{
typedef DirectoryIterator<AllResults
,BasicNonRecursiveEnumeration<SimpleFakeFindXFileFunctions> > testType;
testType begin(L"C:\\Windows\\*");
testType end;
BOOST_CHECK_EQUAL(fakeFunctor::insertedFileName, L"C:\\Windows\\*");
BOOST_CHECK(begin->GetFolderPath() == L"C:\\Windows");
BOOST_CHECK(begin->GetFileName() == L"File.txt");
BOOST_CHECK(begin->GetFullFileName() == L"C:\\Windows\\File.txt");
BOOST_CHECK(begin->GetShortFileName() == L"FILE.TXT");
BOOST_CHECK_EQUAL(begin->GetSize(), 1024);
BOOST_CHECK(begin->IsFile());
BOOST_CHECK(begin != end);
begin++;
BOOST_CHECK(begin->GetFileName() == L"System32");
BOOST_CHECK(begin->GetFullFileName() == L"C:\\Windows\\System32");
BOOST_CHECK(begin->GetShortFileName() == L"SYSTEM32");
BOOST_CHECK_EQUAL(begin->GetSize(), 0);
BOOST_CHECK(begin->IsDirectory());
begin++;
BOOST_CHECK(begin == end);
}
BOOST_AUTO_TEST_CASE( NoRootDirectories )
{
typedef DirectoryIterator<AllResults
,BasicNonRecursiveEnumeration<SimpleFakeFindXFileFunctions> > testType;
fakeFunctor::fakeData.pop_front();
fakeFunctor::fakeData.pop_front();
testType begin(L"C:\\Windows\\*");
testType end;
BOOST_CHECK_EQUAL(fakeFunctor::insertedFileName, L"C:\\Windows\\*");
BOOST_CHECK(begin->GetFolderPath() == L"C:\\Windows");
BOOST_CHECK(begin->GetFileName() == L"File.txt");
BOOST_CHECK(begin->GetFullFileName() == L"C:\\Windows\\File.txt");
BOOST_CHECK(begin->GetShortFileName() == L"FILE.TXT");
BOOST_CHECK_EQUAL(begin->GetSize(), 1024);
BOOST_CHECK(begin->IsFile());
BOOST_CHECK(begin != end);
begin++;
BOOST_CHECK(begin->GetFileName() == L"System32");
BOOST_CHECK(begin->GetFullFileName() == L"C:\\Windows\\System32");
BOOST_CHECK(begin->GetShortFileName() == L"SYSTEM32");
BOOST_CHECK_EQUAL(begin->GetSize(), 0);
BOOST_CHECK(begin->IsDirectory());
begin++;
BOOST_CHECK(begin == end);
}
BOOST_AUTO_TEST_CASE( Empty1 )
{
fakeFunctor::fakeData.clear();
typedef DirectoryIterator<AllResults
,BasicNonRecursiveEnumeration<SimpleFakeFindXFileFunctions> > testType;
testType begin(L"C:\\Windows\\*");
testType end;
BOOST_CHECK(begin == end);
}
BOOST_AUTO_TEST_CASE( Empty2 )
{
fakeFunctor::fakeData.erase(fakeFunctor::fakeData.begin() + 2, fakeFunctor::fakeData.end());
typedef DirectoryIterator<AllResults
,BasicNonRecursiveEnumeration<SimpleFakeFindXFileFunctions> > testType;
testType begin(L"C:\\Windows\\*");
testType end;
BOOST_CHECK(begin == end);
}
BOOST_AUTO_TEST_CASE( Exceptions )
{
typedef DirectoryIterator<AllResults,BasicNonRecursiveEnumeration<ErroneousFindXFileFunctionFirst> >
firstFailType;
BOOST_CHECK_THROW(firstFailType(L"C:\\Windows\\*"), WindowsAPI::ErrorAccessDeniedException);
typedef DirectoryIterator<AllResults,BasicNonRecursiveEnumeration<ErroneousFindXFileFunctionNext> >
nextFailType;
nextFailType constructedOkay(L"C:\\Windows\\*");
BOOST_CHECK_THROW(constructedOkay++, WindowsAPI::ErrorInvalidParameterException);
}
BOOST_AUTO_TEST_CASE( CorrectDestruction )
{
typedef DirectoryIterator<AllResults
,BasicNonRecursiveEnumeration<SimpleFakeFindXFileFunctions> > testType;
testType begin(L"C:\\Windows\\*");
testType end;
BOOST_CHECK(begin != end);
}
BOOST_AUTO_TEST_SUITE_END()