c++ - 如何对移动操作进行单元测试(默认)?
问题描述
当我尝试为只移动类编写单元测试时,我遇到了这个问题。我不知道如何编写一个测试来检查移动操作是否真的移动了类的数据成员。我在这里包含了一个与我正在研究的类相似的类的简化示例。实际上,该类支持更多的操作和 ( std::multimap
) 数据成员,这在此处不应该相关。
#include <vector>
class MyClass {
public:
inline MyClass() = default;
MyClass(const MyClass& other) = delete;
MyClass(MyClass&& other) = default;
MyClass& operator=(const MyClass& other) = delete;
MyClass& operator=(MyClass&& other) = default;
~MyClass() = default;
inline void set(const std::vector<MyStruct>& data) {data_ = data;}
inline const std::vector<MyStruct>& get() {return data_;}
private:
std::vector<MyStruct> data_;
};
MyStruct
仅包含原始数据类型 ( int
,float
和double
) 和一些std::string
类型作为 ( public
) 数据成员。为了完整起见,我MyStruct
在最后添加了一个定义。
我什至不知道如何开始,也无法通过在线搜索找到任何东西。理想情况下,googletest 或 googlemock 解决方案会很棒,但只是一种通用方法或它在其他测试框架中的工作方式可能会帮助我理解它并在我喜欢的框架中实现它。
#include <string>
struct MyStruct {
int foo;
float bar;
double baz;
std::string fips;
};
到目前为止的解决方案(来自下面的评论和答案):
可能的方法1
基于我与@MutableSideEffect 的交流
模拟数据成员并测试在调用移动操作时是否调用了它们MyClass
的移动操作。
这看起来简单明了。这表明(default
ed)移动操作是否MyClass
使用了每个数据成员的移动操作。提供正确的移动操作应该是数据成员类型的责任。
我唯一的问题是,如果不对我的整个代码进行模板化,我不知道如何模拟我无法访问其实现的数据类型(如 google mock docs for mocking non-virtual functions 中所述)。
不过,在这种特殊情况下,我仍然可以模拟MyStruct
并测试在调用移动操作时是否调用了它MyClass
的移动操作。
可能的方法2
基于@Eljay 的回答。
添加一个数据成员,例如Marker mark
,添加到我定义的每个类,或者添加到我想知道在调用类的移动操作时它们的数据成员是否被移动的每个类。指示Marker
类以内部状态记录其构造函数中的哪个构造函数。然后测试是否MyClass
导致其数据成员的内部状态mark
反映它被移动的移动操作。
我不确定这种方法如何完全测试所有数据成员都被移动了,而不仅仅是mark
数据成员。此外,这让我想起了很多只是模拟可以追溯到方法 1 的数据成员。
解决方案
这是一种使用 Marker 成员变量跟踪对象状态的方法。请记住,std::move
这并不意味着对象将被移出,它只会使对象有资格被移出。
一旦一个对象被移出,它就处于“有效但未指定的状态”,适合被破坏或重新分配。(一些标准 C++ 库类型对移出后的状态有稍强的保证,例如std::vector
和std::unique_ptr
。)
#include <cstring>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <cassert>
#ifndef TESTING
#define TESTING 1
#endif
namespace {
struct Marker final {
char const* state;
~Marker() { state = "destructed"; }
Marker() : state{"constructed"} {}
Marker(Marker const&) noexcept : state{"copy constructed"} {}
Marker(Marker&& other) noexcept : state{"move constructed"} { other.state = "move constructed husk"; }
Marker& operator=(Marker const&) noexcept { state = "assigned"; return *this; }
Marker& operator=(Marker&& other) noexcept { state = "move assigned"; other.state = "move assigned husk"; return *this; }
void print(std::ostream&) const;
};
void Marker::print(std::ostream& out) const {
out << state;
}
std::ostream& operator<<(std::ostream& out, Marker const& marker) {
marker.print(out);
return out;
}
void test();
class BigFancyClass {
friend void test();
std::vector<std::string> v;
public:
BigFancyClass() = default;
BigFancyClass(BigFancyClass const&) = default;
BigFancyClass(BigFancyClass&&) = default;
BigFancyClass& operator=(BigFancyClass const&) = default;
BigFancyClass& operator=(BigFancyClass&&) = default;
#if TESTING
Marker mark;
#endif
};
void test() {
std::cout << "Running test()... ";
BigFancyClass bfc;
bfc.v.push_back("hello");
bfc.v.push_back("world");
assert(bfc.v.size() == 2);
assert(std::strcmp(bfc.mark.state, "constructed") == 0);
BigFancyClass bfc2 = std::move(bfc);
assert(bfc.v.size() == 0);
assert(bfc2.v.size() == 2);
assert(std::strcmp(bfc.mark.state, "move constructed husk") == 0);
assert(std::strcmp(bfc2.mark.state, "move constructed") == 0);
BigFancyClass bfc3;
bfc3 = std::move(bfc2);
assert(std::strcmp(bfc2.mark.state, "move assigned husk") == 0);
assert(std::strcmp(bfc3.mark.state, "move assigned") == 0);
std::cout << "DONE\n";
}
} // anon
int main() {
test();
}
推荐阅读
- python - 使用 camelot-py (lattice) 提取 PDF 表:split_text 不起作用
- android - 如何修复他的错误异常:Gradle 任务 assembleDebug 失败,退出代码为 1
- angular - 离子服务不适用于“应用内浏览器”插件
- python - Python程序对矩阵的每一行和每一列求和
- javascript - 如何通过具有firebase功能的FTP下载文件?
- mysql - 加入表,但在 SQL 的一列中保留每一行(从用户表添加他们从订单列表访问的每家餐厅)+ 最新日期
- r - 尽管早期在 CodeAcademy 上工作,Dplyr 脚本未返回 NA 值
- amazon-web-services - API Gateway 在不应该关心我的 Authorization 标头时
- java - 与 Handler 相关的多个编译错误
- list - 在 Kotlin 中制作可变大小的不可变列表