我有许多使用Boost的测试源文件。测试和我试图运行它们,但得到multiple definition of boost::unit_test::runtime_config::argument_store()
错误和这个库的许多模块相同。
例如,有两个文件:test_size.cpp
#define BOOST_TEST_MODULE Test_Repeats
#include "../include/test_config.h"
BOOST_AUTO_TEST_CASE(test_repeats) {
int n = 30;
Repeats r = Repeats(n);
BOOST_REQUIRE(r.rep == n);
}
和test_repeats.cpp
:
#define BOOST_TEST_MODULE Test_Size
#include "../include/test_config.h"
BOOST_AUTO_TEST_CASE(test_size) {
int n = 20;
int m = 30;
Size sz = Size(n, m);
BOOST_REQUIRE(sz.n == n && sz.m == m);
}
和test_config.h
:
#include <boost/test/included/unit_test.hpp>
#include "../include/size.h"
#include "../include/repeats.h"
这是我的Makefile
运行测试:
FAST_HEADERS := $(wildcard Fast/**/*.h)
FAST_TEST_SOURCES := $(filter-out Fast/src/main.cpp, $(wildcard Fast/src/*.cpp Fast/tests/test_size.cpp Fast/tests/test_repeats.cpp))
FAST_TEST_OBJECTS := ${FAST_TEST_SOURCES:.cpp=.o}
fast_test: $(FAST_TEST_OBJECTS)
g++ $(FAST_TEST_OBJECTS) -lboost_unit_test_framework -o fast_test
./fast_test
%.o: Fast/**/%.cpp $(FAST_HEADERS)
g++ $@ -o $<
我知道有可能在一个源文件中有所有的测试,它会工作得很好,但我很好奇是否有可能在不同的源文件中分发所有的测试单元,使其更结构化。
有什么问题和我Makefile
或源文件吗?
%.o: Fast/**/%.cpp $(FAST_HEADERS)
g++ $@ -o $<
由于**
,这永远不适用。你还反转了$@
和$<
。你可以使用
Fast/tests/%.o: Fast/tests/%.cpp | $(FAST_HEADERS)
Fast/src/%.o: Fast/src/%.cpp | $(FAST_HEADERS)
%.o: %.cpp
g++ -o $@ $<
真正的问题
也就是说,让我们找出链接器消息的问题所在。老实说,我花在上的时间远比我愿意承认的"偶然"多得多。找到它。首先,我显然使用了文档,主要是:
- https://www.boost.org/doc/libs/1_53_0/libs/test/doc/html/utf/user-guide/test-runners.html
- 特别是关于UTF的动态库变体"这有用地提到了
对于一个多文件测试模块标志不能在makefile中定义,必须只在一个测试文件中定义,以避免函数
main()
的重复副本。
还有一些邮件列表/SO的帖子:
- boost test library: Multiple definition error
- Boost Test Error: main已经定义
- 和其他
老实说,我学到的唯一切实的东西是
- BOOST_TEST_MODULE只打算在整个测试可执行文件中定义一次
然而,其他记录的标志BOOST_TEST_DYN_LINK
和BOOST_TEST_NO_MAIN
没有任何影响。经过反复试验和预处理器#error
调试后,我发现这些符号在包含
undef
。#include <boost/test/included/unit_test.hpp>
我没有立即认出/included/
部分。一时兴起,我想改成:
#include <boost/test/unit_test.hpp>
工作,但现在main
是未定义。因此,我提出了以下重构的makefile和一个额外的Fast/tests/module.cpp
来定义main。毫无疑问,这一切仍然不是完全理想的。例:我认为,既然你正在链接动态库,BOOST_TEST_DYN_LINK应该被定义,但它似乎没有¯()/¯.
因此,这里显示的Makefile的一部分是为了在需要时如何实现特定高级任务的灵感。
test: # make default target
FAST_HEADERS := $(wildcard Fast/**/*.h)
FAST_TEST_SOURCES := $(filter-out Fast/src/main.cpp, $(wildcard Fast/src/*.cpp Fast/tests/*.cpp))
FAST_TEST_OBJECTS := ${FAST_TEST_SOURCES:.cpp=.o}
CPPFLAGS+= $(INCLUDES)
CPPFLAGS+= -D BOOST_TEST_DYN_LINK
CXXFLAGS+= -std=c++11 $(CPPFLAGS)
LDFLAGS+= -lboost_unit_test_framework
Fast/tests/test_%.o: CPPFLAGS+=-DBOOST_TEST_NO_MAIN
Fast/tests/%.o: Fast/tests/%.cpp | $(FAST_HEADERS)
Fast/src/%.o: Fast/src/%.cpp | $(FAST_HEADERS)
%.o: %.cpp
g++ $(CXXFLAGS) -o $@ -c $<
fast_test: $(FAST_TEST_OBJECTS)
g++ $(CXXFLAGS) $^ $(LDFLAGS) -o $@
test: fast_test
./$<
.PHONY: test
使用make -Bsn
测试显示了标志如何组合:
g++ -std=c++11 -D BOOST_TEST_DYN_LINK -o Fast/src/size.o -c Fast/src/size.cpp
g++ -std=c++11 -D BOOST_TEST_DYN_LINK -o Fast/src/repeats.o -c Fast/src/repeats.cpp
g++ -std=c++11 -D BOOST_TEST_DYN_LINK -DBOOST_TEST_NO_MAIN -o Fast/tests/test_size.o -c Fast/tests/test_size.cpp
g++ -std=c++11 -D BOOST_TEST_DYN_LINK -DBOOST_TEST_NO_MAIN -o Fast/tests/test_repeats.o -c Fast/tests/test_repeats.cpp
g++ -std=c++11 -D BOOST_TEST_DYN_LINK -o Fast/tests/module.o -c Fast/tests/module.cpp
g++ -std=c++11 -D BOOST_TEST_DYN_LINK Fast/src/size.o Fast/src/repeats.o Fast/tests/test_size.o Fast/tests/test_repeats.o Fast/tests/module.o
-lboost_unit_test_framework -o fast_test
./fast_test
例如./fast_test -l all
:
Running 2 test cases...
Entering test module "Fast_Tests"
Fast/tests/test_size.cpp(3): Entering test case "test_size"
Fast/tests/test_size.cpp(8): info: check sz.n == n && sz.m == m has passed
Fast/tests/test_size.cpp(3): Leaving test case "test_size"; testing time: 136us
Fast/tests/test_repeats.cpp(3): Entering test case "test_repeats"
Fast/tests/test_repeats.cpp(7): info: check r.rep == n has passed
Fast/tests/test_repeats.cpp(3): Leaving test case "test_repeats"; testing time: 125us
Leaving test module "Fast_Tests"; testing time: 311us
*** No errors detected
收场
我保持了"测试驱动";Size
/Repeats
头文件的实现。如果要在实际代码中更改,则需要链接依赖对象。
对于我没有解释的GNU Make特性,请参阅https://www.gnu.org/software/make/manual/
代码转储:
File
Makefile
test: # make default target FAST_HEADERS := $(wildcard Fast/**/*.h) FAST_TEST_SOURCES := $(filter-out Fast/src/main.cpp, $(wildcard Fast/src/*.cpp Fast/tests/*.cpp)) FAST_TEST_OBJECTS := ${FAST_TEST_SOURCES:.cpp=.o} CPPFLAGS+= $(INCLUDES) CPPFLAGS+= -D BOOST_TEST_DYN_LINK CXXFLAGS+= -std=c++11 $(CPPFLAGS) LDFLAGS+= -lboost_unit_test_framework Fast/tests/test_%.o: CPPFLAGS+=-DBOOST_TEST_NO_MAIN Fast/tests/%.o: Fast/tests/%.cpp | $(FAST_HEADERS) Fast/src/%.o: Fast/src/%.cpp | $(FAST_HEADERS) %.o: %.cpp g++ $(CXXFLAGS) -o $@ -c $< fast_test: $(FAST_TEST_OBJECTS) g++ $(CXXFLAGS) $^ $(LDFLAGS) -o $@ test: fast_test ./$< .PHONY: test
File
Fast/include/repeats.h
#pragma once struct Repeats { explicit Repeats(int n) : rep(n){} int const rep; };
File
Fast/include/size.h
#pragma once struct Size { explicit Size(int n, int m) : n(n) , m(m) { } int const n, m; };
File
Fast/include/test_config.h
#include <boost/test/unit_test.hpp> #include "../include/size.h" #include "../include/repeats.h"
File
Fast/src/main.cpp
File
Fast/src/repeats.cpp
文件File
Fast/tests/module.cpp
#define BOOST_TEST_MODULE Fast_Tests #include <boost/test/included/unit_test.hpp>
文件
Fast/tests/test_repeats.cpp
#include "../include/test_config.h" BOOST_AUTO_TEST_CASE(test_repeats) { int n = 30; Repeats r = Repeats(n); BOOST_REQUIRE(r.rep == n); }
文件#include "../include/test_config.h" BOOST_AUTO_TEST_CASE(test_size) { int n = 20; int m = 30; Size sz = Size(n, m); BOOST_REQUIRE(sz.n == n && sz.m == m); }
Fast/src/size.cpp
Fast/tests/test_size.cpp