我是第一次尝试使用C ++进行单元测试,而且我多年没有使用过C ++(我现在主要是C#编码器)。看起来我正在做一个正确的猪耳朵 - 我希望有人能引导我回到正义的道路上。我刚刚开始在这里开始,并且真的希望使用最佳实践来实现这些测试,所以欢迎任何和所有评论,即使我目前最关心的是我的链接器错误。
所以,我有一个整体解决方案“Technorabble”,子项目“CalibrationTool”和“CalibrationToolUnitTests”。
CalibrationTool有一个MathUtils.h文件:
#ifndef __math_utils__
#define __math_utils__
#include "stdafx.h"
#include <vector>
namespace Technorabble
{
namespace CalibrationTool
{
double GetDoubleVectorAverage(std::vector<double> v)
{
double cumulativeValue = 0;
for(std::vector<double>::iterator iter = v.begin(); iter != v.end(); ++iter)
{
cumulativeValue += *iter;
}
return cumulativeValue / v.size();
}
}; // end namespace CalibrationTool
}; // end namespace Technorabble
#endif // !__math_utils__
(但没有.cpp文件,因为我遇到了各种(有些类似的)问题,导致我的模板功能正常工作 - 所以我最终定义了内联版本。)
继续单元测试项目,我有一个main.cpp:
#include "MathUtilsTest.h"
void RunMathUtilsTests();
int main()
{
RunMathUtilsTests();
// Other class tests will go here when I have things to test
}
void RunMathUtilsTests()
{
MathUtilsTest* mathUtilsTest = new MathUtilsTest();
mathUtilsTest->RunTests();
delete mathUtilsTest;
}
最后,MathUtilsTest类的标题和cpp再次非常简单:
·H:
#ifndef __MATH_UTILS_TEST__
#define __MATH_UTILS_TEST__
#include "CalibrationToolUnitTestsLogging.h"
#include "..\CalibrationTool\MathUtils.h"
class MathUtilsTest
{
public:
MathUtilsTest();
~MathUtilsTest();
bool RunTests();
private:
bool GetDoubleVectorAverageTest();
}; // end class MathUtilsTest
#endif
的.cpp:
#include "MathUtilsTest.h"
#include <sstream>
bool MathUtilsTest::RunTests()
{
return GetDoubleVectorAverageTest();
}
MathUtilsTest::~MathUtilsTest()
{
}
MathUtilsTest::MathUtilsTest()
{
}
bool MathUtilsTest::GetDoubleVectorAverageTest()
{
bool passed = true;
std::vector<double> values;
for (int i = 1; i < 23; i++)
{
values.push_back(i);
}
// vector becomes: 1, 2, 3, 4, .....20, 21, 22. Average is 11.5
double expectedAverage = 11.5;
double calculatedAverage = Technorabble::CalibrationTool::GetDoubleVectorAverage(values);
if (calculatedAverage != expectedAverage)
{
std::ostringstream s;
s << calculatedAverage;
std::string avgString = s.str();
CalibrationToolUnitTestsLogging::Write("Failed MathUtilsTest.GetDoubleVectorAverageTest: " + avgString);
passed = false;
}
else
{
CalibrationToolUnitTestsLogging::Write("Passed MathUtilsTest.GetDoubleVectorAverageTest");
}
return passed;
}
这对我来说似乎很好,我用#ifndef等保护我的标题但是我仍然遇到以下错误:
1)错误LNK1169:找到一个或多个多重定义的符号
2)错误LNK2005:“double __cdecl Technorabble :: CalibrationTool :: GetDoubleVectorAverage(class std :: vector&gt;)”(?GetDoubleVectorAverage @ CalibrationTool @ Technorabble @@ YANV?$ vector @ NV?$ allocator @ N @ std @@@ std @@@ Z)已在main.obj中定义C:_SVN \ Technorabble \ Windows Software \ CalibrationToolUnitTests \ MathUtilsTest.obj
这怎么可能?谁能发现它出错的地方?
答案 0 :(得分:5)
标题中定义的函数应标记为inline
:
inline double GetDoubleVectorAverage(std::vector<double> v)
{
}
如果它长于几行,请考虑将其移至实施文件。
pragma
或包含警卫不能防止多种定义。
请注意,您应该通过v
引用而不是按值传递const
。
答案 1 :(得分:3)
您正在标题中定义函数GetDoubleVectorAverage
。这意味着它将在包含该标题的每个翻译单元(即每个源文件)中定义。如果您的程序包含多个此类翻译单元,那么您将拥有多个定义 - 这是不允许的。
解决方案是:
inline
添加到函数定义中,以放宽此规则并允许多个相同的定义;或我用
保护我的标题#ifndef
这只会阻止标题在同一个翻译单元中被多次包含。它不会阻止来自多个单元的包含。
此外,你不应该使用像__math_utils__
这样的inline
作为标题保护,即使互联网上充斥着狡猾的代码这样做的例子。
我遇到各种(有些类似的)问题让我的模板功能正常工作
通常需要在头文件中定义模板,以便在使用时提供定义。函数模板隐式{{1}},但正常函数(如此函数)不是。