Tôi đang sử dụng Boost :: Kiểm tra thư viện cho kiểm tra đơn vị, và tôi đã nói chung được hack lên các giải pháp chế nhạo của riêng tôi mà giống như thế này:Mocking với Boost :: Kiểm tra
//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
}
tôi đã trở nên thất vọng với điều này bởi vì nó đòi hỏi tôi phải thực hiện gần như tất cả mọi thứ như một mẫu, và nó là rất nhiều mã soạn sẵn để đạt được những gì tôi đang tìm kiếm.
Có cách nào tốt để tạo mã bằng cách sử dụng Boost :: Test over my Ad-hoc method?
Tôi đã thấy một số người đề xuất Google Mock, nhưng nó đòi hỏi rất nhiều hack xấu xí nếu chức năng của bạn không phải là virtual
, mà tôi muốn tránh.
Oh: Một điều cuối cùng. Tôi không cần khẳng định rằng một đoạn mã cụ thể được gọi. Tôi chỉ cần có thể tiêm dữ liệu mà thông thường sẽ được trả về bởi các hàm Windows API.
EDIT: Đây là một bộ ví dụ lớp và các bài kiểm tra tôi có cho nó:
Các lớp dưới kiểm tra:
#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) {
};
};
}}
Các xét nghiệm cho lớp này:
#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()
Nếu bạn muốn thử các cuộc gọi Windows API, tôi sẽ không lo lắng về việc thực hiện các chức năng ảo. Tại sao bạn tránh điều này? –
@epronk: Đơn giản vì không có lý do gì để làm như vậy - nó tệ hơn phương pháp hiện tại của tôi. Ít nhất với các mẫu không có hình phạt thời gian chạy. –
Vì vậy, các bài kiểm tra của bạn chạy rất nhanh. Biên soạn và liên kết thử nghiệm mất bao lâu? –