我遇到了Google Test的问题。我有一个等同于以下内容的代码:
foreach (string row in File.ReadAllLines(fullPath))
{
if (!string.IsNullOrEmpty(row))
{
dt.Rows.Add();
int i = 0;
foreach (string cell in row.Split('\t'))
{
dt.Rows[dt.Rows.Count - 1][i] = cell;
i++;
}
}
}
template<int N> class A {
public:
A() {}
A(int n) {
var = n;
}
int var;
};
总而言之,我想在#include "gtest/gtest.h"
#include "A.h"
template<typename T> class TestA : public ::testing::Test {};
TYPED_TEST_CASE_P(TestA);
TYPED_TEST_P(TestA, SomeTest){
TypeParam x(0);
EXPECT_EQ(x.var,0);
...
}
REGISTER_TYPED_TEST_CASE_P(TestA, SomeTest);
typedef ::testing::Types<A<0>, A<1>, A<2>, A<3>, A<4>, A<5> ... A<511>, A<512>> MyTypes;
INSTANTIATE_TYPED_TEST_CASE_P(My, TestA, MyTypes);
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
的许多版本的SomeTest
课程上运行测试A
。对于上面提到的type-parameterized test,问题在于&#34;类型测试&#34;方法,我必须手动编写我要测试的类N
的所有版本。
使用Google Test,您还可以编写value-parameterized tests,这非常方便,因为您可以使用A
等生成器:
Range
然后,您可以使用测试中的函数INSTANTIATE_TEST_CASE_P(InstantiationName, TestA, ::testing::Range(0,512,1));
来访问这些值。这种语法将使我想要做的事情很容易设置。但是,似乎在编译时未解析这些值,因此它们无法用于指定模板值。
我的问题是:
GetParam()
的所有版本?A
类型列表?N.B:必须符合c ++ 11标准。
答案 0 :(得分:0)
你正在寻找一种方法来节省googletest中的指法 通过避免类型列表的硬编码,您已在上面概述了解决方案:
A<0>, A<1>, A<2>, A<3>, A<4>, A<5>, ... A<511>, A<512>
在MyTypes
的定义中,并以某种方式在编译时生成
只给出了类型列表的长度。
令人遗憾的是,你可以通过解决来节省非常有限的指法 这个问题。在内部,googletest专门使用C ++ 03。所以没有变量 模板,与C ++ 11一起到达。因此,有一个硬编码限制 类型列表的长度:
::testing::Types<T0, T1, ... Tn>
您需要513种类型的列表:硬编码限制为50.在编译之后 失败。
尽管如此,如果您要使用此类googletest解决方案做很多事情, 如果类型列表长度在N x 10范围内,则可能值得您使用代码 有货可以按照你想要的方式生成它们。
C ++ 14只是这类工作的工具。它是template< class T, T... Ints> class std::integer_sequence
。
但是你说你被限制在C++11
。在那种情况下,你需要一个
手卷代替std::integer_sequence
。那里有很多
在Google土地上。 Jonathan Wakeley's one
脱颖而出:std::integer_sequence
是他的C ++ 14提案,以及他的实施
是C ++ 14标准的原型。
现在我要用自己的杂草来做。这是一个头文件:
<强> integer_seq.h 强>
#ifndef INTEGER_SEQ_H
#define INTEGER_SEQ_H
#if __cplusplus >= 201402L
#include <utility>
template<typename IntType, IntType ...Is>
using integer_seq = std::integer_sequence<IntType,Is...>;
template<typename IntType, IntType N>
using make_integer_seq = std::make_integer_sequence<IntType,N>;
#elif __cplusplus == 201103L
#include <type_traits>
template<typename IntType, IntType ...Is> struct integer_seq {};
namespace detail {
template<typename IntType, IntType Count, bool Done, IntType ...Is>
struct gen_seq;
template<typename IntType, IntType Count, IntType ...Is>
struct gen_seq<IntType, Count, false, Is...>
{
static_assert(Count > 0,"Count must be positive");
using type =
typename gen_seq< IntType,
Count - 1, Count - 1 == 0,
Count - 1, Is...
>::type;
};
template <typename IntType, IntType Count, IntType ...Is>
struct gen_seq<IntType, Count, true, Is...>
{
using type = integer_seq<IntType,Is...>;
};
} // namespace detail
template<typename IntType, IntType N>
using make_integer_seq = typename detail::gen_seq<IntType,N,N == 0>::type;
#else
#error At least C++11 required :(
#endif
#endif
此标题定义:
template< typename IntType, IntType ...Is> struct integer_seq;
表示某些整数类型Is...
的值的序列IntType
,并且:
template<typename IntType, IntType N> make_integer_seq;
是类型的别名:
integer_sequence<IntType,0,... N - 1>
如果在C ++ 14中编译,则integer_seq
本身只是一个别名
std::integer_sqeuence
。如果C ++ 11那么integer_seq
是手动的。
一旦您获得了模板integer_seq
,就可以使用它来获得一些杠杆作用
在googletest的模板::testing::Types
上。这是另一个头文件:
<强> indexed_type_list 强>
#ifndef INDEXED_TYPE_LIST_H
#define INDEXED_TYPE_LIST_H
#include <cstddef>
#include "gtest/gtest.h"
#include "integer_seq.h"
template<typename IntType, template<IntType> class T, typename Seq>
struct indexed_type_list;
template<typename IntType, template<IntType> class T, IntType ...Is>
struct indexed_type_list<IntType,T,integer_seq<IntType,Is...>>
{
using type = ::testing::Types<T<Is>...>;
};
template<typename IntType, template<IntType> class T, std::size_t Size>
using indexed_type_list_t =
typename indexed_type_list<IntType,T,make_integer_seq<IntType,Size>>::type;
#endif
定义:
template<typename IntType, template<IntType> class T, IntType Size>
indexed_type_list_t;
这样,给定一些整数类型IntType
,该类型的数字Size
,
和一些一元非类型模板template<IntType> class T
,然后:
indexed_type_list_t<IntType,T,Size>
是:
的别名::testing::Types<T<0>,... T<Size - 1>>;
这就是你所追求的。 indexed_type_list
无疑是一个非常蹩脚的名字
对于这个概念,但我的想象力让我失望。
现在我们准备好了。以下是我们如何调整您的标本解决方案:
<强>的main.cpp 强>
#include "indexed_type_list.h"
#include "A.h"
template<typename T> class TestA : public ::testing::Test{};
TYPED_TEST_CASE_P(TestA);
TYPED_TEST_P(TestA, SomeTest){
TypeParam x(0);
EXPECT_EQ(x.var,0);
}
REGISTER_TYPED_TEST_CASE_P(TestA, SomeTest);
using MyTypes = indexed_type_list_t<int,A,50>;
INSTANTIATE_TYPED_TEST_CASE_P(My, TestA, MyTypes);
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
编译和链接:
$ g++ -Wall -Wextra -pedantic -std=c++11 -c main.cpp
$ g++ -o testrunner main.o -lgtest -pthread
它的运行方式如下:
$ ./testrunner
[==========] Running 50 tests from 50 test cases.
[----------] Global test environment set-up.
[----------] 1 test from My/TestA/0, where TypeParam = A<0>
[ RUN ] My/TestA/0.SomeTest
[ OK ] My/TestA/0.SomeTest (0 ms)
[----------] 1 test from My/TestA/0 (0 ms total)
[----------] 1 test from My/TestA/1, where TypeParam = A<1>
[ RUN ] My/TestA/1.SomeTest
[ OK ] My/TestA/1.SomeTest (0 ms)
[----------] 1 test from My/TestA/1 (0 ms total)
...
...
[----------] 1 test from My/TestA/48, where TypeParam = A<48>
[ RUN ] My/TestA/48.SomeTest
[ OK ] My/TestA/48.SomeTest (0 ms)
[----------] 1 test from My/TestA/48 (0 ms total)
[----------] 1 test from My/TestA/49, where TypeParam = A<49>
[ RUN ] My/TestA/49.SomeTest
[ OK ] My/TestA/49.SomeTest (0 ms)
[----------] 1 test from My/TestA/49 (0 ms total)
[----------] Global test environment tear-down
[==========] 50 tests from 50 test cases ran. (0 ms total)
[ PASSED ] 50 tests.
这样你的问题就解决了。它在这里都是可选的。
反面
这种测试任何类模板的方法存在致命的设计缺陷
在一系列非类型模板参数上。在我们解决之前它就在那里
为::testing::Types
生成类型列表的问题,它仍然存在
那里。
在实际应用中,经过精心设计,你不会遇到类似的类模板
template<int N> class A
中定义的A.h
,其中模板参数N
在模板定义或模板特化中根本不使用。
该模板参数没有实际意义。 A
的定义可能会
以及:
class A {
public:
A() {}
A(int n) {
var = n;
}
int var;
};
假设我们不得不处理A
而不是更多
逼真的模板:
<强> dot_matrix.h 强>
#ifndef DOT_MATRIX_H
#define DOT_MATRIX_H
#include <array>
template<std::size_t N>
struct dot_matrix
{
bool get(std::size_t x, std::size_t y) const {
return _square.at(x).at(y);
}
void set(std::size_t x, std::size_t y) {
_square.at(x).at(y) = true;
++_filled;
}
void clear(std::size_t x, std::size_t y){
_square.at(x).at(y) = false;
--_filled;
}
unsigned filled() const {
return _filled;
}
unsigned vacant() const {
return area() - _filled;
}
static constexpr std::size_t side() {
return N * 2;
}
static constexpr std::size_t area() {
return side() * side();
}
private:
unsigned _filled;
std::array<std::array<bool,N>,N> _square;
};
#endif
据说,template<std::size_t N> struct dot_matrix
一般来说
封装大小为N
x N
的固定大小的方点阵。我说应该是
因为现在它被一个错误所遗忘 - 真的,一个不切实际的明显错误
- 我们希望通过googletest单元测试进行清理。
您绘制吸管以对单元测试进行编码并完成所有设置
TYPED_TEST_CASE_P
样式测试,用:
using MyTypes = ::testing::Types<dot_matrix<0>,... dot_matrix<49>>;
是否编译时生成
并不重要dot_matrix<0>,... dot_matrix<49>
或严格编码。
你写了一堆TYPED_TEST_P
,例如:
TYPED_TEST_P(TestDotMatrix,IsSquare){
TypeParam matrix;
auto side = matrix.side();
EXPECT_EQ(matrix.area(),side * side));
}
TYPED_TEST_P(TestDotMatrix, IsConsistent){
TypeParam matrix;
auto cap = matrix.filled() + matrix.vacant();
EXPECT_EQ(matrix.area(),cap));
}
...
团队负责人代码审核和点: -
dot_matrix<N>
中的错误。它没有封装N
x N
点阵。
实际上,它封装了2N
x 2N
点阵。
您还没有通过测试来验证dot_matrix<N>
是否包含
N
x N
矩阵。
你回过头来解决这个疏忽: -
TYPED_TEST_P(TestDotMatrix, SizeIsRight){
TypeParam matrix;
EXPECT_EQ(???,matrix.side());
}
但你不能。您无法填写???
,因为TypeParam
只是
dot_matrix<N>
表示测试不知道的N
的某些值,N
需要替换???
。
因为没有一个测试知道N
与TypeParam
的价值
实例化后,他们都可以只测试dot_matrix<N>
的行为
对于N
是不变的,就像那样:
TYPED_TEST_P(TestA, SomeTest){
TypeParam x(0);
EXPECT_EQ(x.var,0);
}
的上方。测试50个不同值的N
不变的行为
N
并不有用。当然, 对于那些N
- 不变行为进行测试很有用。
只是不是N
的50个不同值,或者实际上不是一个。{/ p>
做的行为因N
而异的行为也需要进行测试。喜欢:是的
dot_matrix<N>
报告 N
的正确大小和区域?
对于这些测试,您的googletest解决方案需要一个范围
在N
的实例化中变化TYPED_TEST_P
。这是需要的
我们感谢您将TYPED_TEST_P
排除在&#39; ???奎德里: -
template struct dot_matrix<std::size_t N>
正在接受测试的事实是不变。
这些信息都不需要提供测试:测试范围不需要
传达它。唯一不同的是N
。 N
的值是每个测试需要的一件事
知道。实例化测试所需的范围是一系列值 N
。
这对于googletest解决方案来说乍看起来很困难。
Googletest提供了一个生成Value Parameterized Tests的框架
可以为给定的值范围中的每一个实例化TEST_P
作为::testing::Values(...)
的运行时参数。
Googletest还提供了生成Typed Tests的框架
和Type Parameterized Tests
可以为给定的类型范围中的每一个实例化TYPED_TEST
或TYPED_TEST_P
as {em> compiletime 模板参数::testing::Types<...>
。
但我们需要一系列非类型模板参数的值。 C ++需要 这样的参数是一个编译时积分常量。测试范围的googletest选项 不包括一系列编译时积分常量。
令人高兴的是,C ++ 11或更高版本采用方式来映射整数
唯一一个类型。它是template< class IntType, IntType v > struct std::integral_constant
。
对于任何常数N
的整数类型IntType
:
struct std::integral_constant<IntType,N>;
是唯一映射的类型,从该类型可以检索编译时
常数N
as:
std::integral_constant<IntType,N>::value;
所以这里只是googletest解决方案:
...
using MyTypes =
::testing::Types<std::integral_constant<0>,...std::integral_constant<49>>;
...
...
TYPED_TEST_P(TestDotMatrix, SizeIsRight){
dot_matrix<TypeParam::value> matrix;
EXPECT_EQ(TypeParam::value,matrix.side());
}
这是一个完整的示例,testing::Types
范围
像以前一样生成compiletime。
新标题:
<强> integral_constant_typelist.h 强>
#ifndef INTEGRAL_CONSTANT_TYPELIST_H
#define INTEGRAL_CONSTANT_TYPELIST_H
#include <cstddef>
#include <type_traits>
#include "gtest/gtest.h"
#include "integer_seq.h"
template<typename IntType, IntType Start,typename Seq>
struct integral_constant_typelist;
template<typename IntType, IntType Start,IntType ...Is>
struct integral_constant_typelist<IntType,Start,integer_seq<IntType,Is...>>
{
using type =
::testing::Types<std::integral_constant<IntType,Start + Is>...>;
};
template<typename IntType, IntType Start,std::size_t Size>
using integral_constant_typelist_t =
typename integral_constant_typelist<
IntType,Start,make_integer_seq<IntType,Size>
>::type;
#endif
这个标题与indexed_type_list.h
不同,名称并不严重。它&#39; S
googletest工具包的候选人。它定义了:
template<typename IntType, IntType Start,std::size_t Size>
integral_constant_typelist_t
是以下类型:
::testing::Types<std::integral_constant<IntType,Start>,...
std::integral_constant<IntType,Start + (Size - 1)>
所以,例如。
integral_constant_typelist_t<int,3,10>
的类型为:
::testing::Types<
std::integral_constant<int,3>,...std::integral_constant<int,12>>
然后这是dot_matrix<N>
的测试套件:
<强>的main.cpp 强>
#include "integral_constant_typelist.h"
#include "dot_matrix.h"
using arbitrary = std::integral_constant<std::size_t,42>;
// A fixture template for N-variant tests
template<
typename T // = `std::integral_constant<std::size_t, N>`, for some `N`
> struct TestDotMatrixVariant : ::testing::Test
{
using int_type = typename T::value_type; // = std::size_t
static constexpr int_type N_param() {
return T::value; // = N
}
using test_type = dot_matrix<N_param()>;
dot_matrix<N_param()> const & get_specimen() const {
return _specimen;
}
dot_matrix<N_param()> & get_specimen() {
return _specimen;
}
protected:
test_type _specimen;
};
// A fixture for invariant tests
struct TestDotMatrixInvariant : TestDotMatrixVariant<arbitrary>{};
// Invariant test
TEST_F(TestDotMatrixInvariant,IsSquare){
auto const & specimen = get_specimen();
auto side = specimen.side();
EXPECT_EQ(specimen.area(),side * side);
}
// Another invariant test
TEST_F(TestDotMatrixInvariant, IsConsistent){
auto const & specimen = get_specimen();
auto cap = specimen.filled() + specimen.vacant();
EXPECT_EQ(specimen.area(),cap);
}
// Yet another invariant test
TEST_F(TestDotMatrixInvariant,OutOfRangeGetXThrows)
{
auto const & specimen = get_specimen();
auto x = specimen.side() + 1;
EXPECT_THROW(specimen.get(x,0),std::out_of_range);
}
// More invariant tests...
// An N-variant test case
TYPED_TEST_CASE_P(TestDotMatrixVariant);
// An N-variant test
TYPED_TEST_P(TestDotMatrixVariant,SizeIsRight){
EXPECT_EQ(this->N_param(),this->get_specimen().side());
}
REGISTER_TYPED_TEST_CASE_P(TestDotMatrixVariant,SizeIsRight);
using dot_matrices_0_50 = integral_constant_typelist_t<std::size_t,0,50>;
INSTANTIATE_TYPED_TEST_CASE_P(N_0_to_50,TestDotMatrixVariant,dot_matrices_0_50);
// More N-variant test cases and N-variant tests...
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
编译和链接:
$ g++ -Wall -Wextra -pedantic -std=c++11 -c main.cpp
$ g++ -o testrunner main.o -lgtest -pthread
执行命令
$ ./testrunner
[==========] Running 53 tests from 51 test cases.
[----------] Global test environment set-up.
[----------] 3 tests from TestDotMatrixInvariant
[ RUN ] TestDotMatrixInvariant.IsSquare
[ OK ] TestDotMatrixInvariant.IsSquare (0 ms)
[ RUN ] TestDotMatrixInvariant.IsConsistent
[ OK ] TestDotMatrixInvariant.IsConsistent (0 ms)
[ RUN ] TestDotMatrixInvariant.OutOfRangeGetXThrows
[ OK ] TestDotMatrixInvariant.OutOfRangeGetXThrows (0 ms)
[----------] 3 tests from TestDotMatrixInvariant (0 ms total)
[----------] 1 test from N_0_to_50/TestDotMatrixVariant/0, where TypeParam = std::integral_constant<unsigned long, 0ul>
[ RUN ] N_0_to_50/TestDotMatrixVariant/0.SizeIsRight
[ OK ] N_0_to_50/TestDotMatrixVariant/0.SizeIsRight (0 ms)
[----------] 1 test from N_0_to_50/TestDotMatrixVariant/0 (0 ms total)
[----------] 1 test from N_0_to_50/TestDotMatrixVariant/1, where TypeParam = std::integral_constant<unsigned long, 1ul>
[ RUN ] N_0_to_50/TestDotMatrixVariant/1.SizeIsRight
main.cpp:58: Failure
Expected equality of these values:
this->N_param()
Which is: 1
this->get_specimen().side()
Which is: 2
[ FAILED ] N_0_to_50/TestDotMatrixVariant/1.SizeIsRight, where TypeParam = std::integral_constant<unsigned long, 1ul> (0 ms)
[----------] 1 test from N_0_to_50/TestDotMatrixVariant/1 (0 ms total)
...
...
...
[----------] 1 test from N_0_to_50/TestDotMatrixVariant/49, where TypeParam = std::integral_constant<unsigned long, 49ul>
[ RUN ] N_0_to_50/TestDotMatrixVariant/49.SizeIsRight
main.cpp:58: Failure
Expected equality of these values:
this->N_param()
Which is: 49
this->get_specimen().side()
Which is: 98
[ FAILED ] N_0_to_50/TestDotMatrixVariant/49.SizeIsRight, where TypeParam = std::integral_constant<unsigned long, 49ul> (0 ms)
[----------] 1 test from N_0_to_50/TestDotMatrixVariant/49 (0 ms total)
[----------] Global test environment tear-down
[==========] 53 tests from 51 test cases ran. (1 ms total)
[ PASSED ] 4 tests.
[ FAILED ] 49 tests, listed below:
[ FAILED ] N_0_to_50/TestDotMatrixVariant/1.SizeIsRight, where TypeParam = std::integral_constant<unsigned long, 1ul>
...
...
...
[ FAILED ] N_0_to_50/TestDotMatrixVariant/49.SizeIsRight, where TypeParam = std::integral_constant<unsigned long, 49ul>
49 FAILED TESTS
所有不变测试都通过了。对于SizeIsRight
,变体测试dot_matrix<0>
应该通过。然后呢
dot_matrix<1>
到dot_matrix<49>
已失败,
更好的调试:
static constexpr std::size_t side() {
// return N * 2; <-- Wrong
return N; // <-- Right
}
然后:
$ ./testrunner
[==========] Running 53 tests from 51 test cases.
[----------] Global test environment set-up.
[----------] 3 tests from TestDotMatrixInvariant
[ RUN ] TestDotMatrixInvariant.IsSquare
[ OK ] TestDotMatrixInvariant.IsSquare (0 ms)
[ RUN ] TestDotMatrixInvariant.IsConsistent
[ OK ] TestDotMatrixInvariant.IsConsistent (0 ms)
[ RUN ] TestDotMatrixInvariant.OutOfRangeGetXThrows
[ OK ] TestDotMatrixInvariant.OutOfRangeGetXThrows (0 ms)
[----------] 3 tests from TestDotMatrixInvariant (0 ms total)
[----------] 1 test from N_0_to_50/TestDotMatrixVariant/0, where TypeParam = std::integral_constant<unsigned long, 0ul>
[ RUN ] N_0_to_50/TestDotMatrixVariant/0.SizeIsRight
[ OK ] N_0_to_50/TestDotMatrixVariant/0.SizeIsRight (0 ms)
[----------] 1 test from N_0_to_50/TestDotMatrixVariant/0 (0 ms total)
...
...
...
[----------] 1 test from N_0_to_50/TestDotMatrixVariant/49, where TypeParam = std::integral_constant<unsigned long, 49ul>
[ RUN ] N_0_to_50/TestDotMatrixVariant/49.SizeIsRight
[ OK ] N_0_to_50/TestDotMatrixVariant/49.SizeIsRight (0 ms)
[----------] 1 test from N_0_to_50/TestDotMatrixVariant/49 (0 ms total)
[----------] Global test environment tear-down
[==========] 53 tests from 51 test cases ran. (1 ms total)
[ PASSED ] 53 tests.
一切都很好。