如何创建一个 sqlite3_value 以使用 gtest 进行测试

Posted

技术标签:

【中文标题】如何创建一个 sqlite3_value 以使用 gtest 进行测试【英文标题】:How to create a sqlite3_value for testing with gtest 【发布时间】:2016-01-07 10:11:08 【问题描述】:

我正在为 sqlite3 编写一个包装类,只有 4 种乐趣和学习 c++11 和 gtest。我用 gtest 测试我的包装器。在一个特定的测试用例中,我需要一个 sqlite3_value 对象来比较我的函数结果。我不知道如何从头开始创建 sqlite3_value 对象。我搜索了互联网并找到了这个解决方案:

sqlite3 *tmpDB;
sqlite3_open_v2(":memory", &tmpDB, SQLITE_OPEN_READWRITE, 0);
sqlite3_stmt *tmpStmt = nullptr;

sqlite3_prepare_v2(tmpDB, "SELECT ?;", 9, &tmpStmt, nullptr);
sqlite3_bind_text(tmpStmt, 0, "paul", 4, SQLITE_TRANSIENT);
int res = sqlite3_step(tmpStmt);
std::string s(reinterpret_cast<const char*>(sqlite3_value_text(sqlite3_column_value(tmpStmt, 0)))); /*Here throws the exception below*/
sqlite3_finalize(tmpStmt);

在调用 sqlite3_finalize() 之前进行字符串构造时会抛出以下异常:

C++ exception with description "basic_string::_M_construct null not valid" thrown in the test body.

我尝试了堆栈溢出的解决方案: Stack Overflow std::basic_string solution 但我有同样的例外。

我不确定是否可以从头开始构建 sqlite3_value。我尝试“构建” sqlite3_value 的方式是正确的吗?

也许我必须考虑使用其他类型作为 std::string。我读到了一些原因,例如 std::string 不支持文本女巫不是普通的 ACSII。对吗?

有什么建议吗?

【问题讨论】:

【参考方案1】:

首先要做的事情:您引用的异常意味着您将nullptr 传递给std::string 构造函数。这是禁止的。您传递空指针的原因是您在不代表 STRING 值的值上使用了sqlite3_value_text。这又是查询失败造成的。

CL目前对答案的修订有很多好的方面。让我们从最重要的建议开始:

此外,您必须检查所有函数的返回值是否有错误。

您说您正在使用 Google Test,所以让我们使用 EXPECT_EQ 语句来确定函数是否确实返回了您期望的值。这是一个添加了错误检查的完整最小示例:

#include <sqlite3.h>
#include <string>
#include <gtest/gtest.h>

int main(void)

  sqlite3 *tmpDB;
  EXPECT_EQ(SQLITE_OK, sqlite3_open_v2(":memory", &tmpDB, SQLITE_OPEN_READWRITE, 0));
  sqlite3_stmt *tmpStmt = nullptr;

  EXPECT_EQ(SQLITE_OK, sqlite3_prepare_v2(tmpDB, "SELECT ?;", 9, &tmpStmt, nullptr));
  EXPECT_EQ(SQLITE_OK, sqlite3_bind_text(tmpStmt, 0, "paul", 4, SQLITE_TRANSIENT));
  int res = sqlite3_step(tmpStmt);
  EXPECT_EQ(SQLITE_ROW, res);
  std::string s(reinterpret_cast<const char*>(sqlite3_value_text(sqlite3_column_value(tmpStmt, 0))));
  EXPECT_EQ(SQLITE_OK, sqlite3_finalize(tmpStmt));

这会输出很多期望失败。哎呀!第一个期望失败是 sqlite_open 返回 14 (SQLITE_CANTOPEN)。仔细阅读CL给出的答案可以找到原因,其实仔细阅读第一句话应该就够了。

修复该问题后,sqlite3_bind_text 上的 EXPECT_EQ 失败,错误代码为 25 (SQLITE_RANGE)。阅读 sqlite3_bind_text 的文档,尤其是描述第二个参数的段落的前两句,找出这个调用有什么问题。

也解决了这个问题,我不再遇到异常或任何预期失败。

【讨论】:

【参考方案2】:

内存数据库的名称不是:memory,而是:memory:

sqlite3_bind_text()的最后一个参数不能为NULL。

但您的问题是查询从未执行。

另外,如sqlite3_column_value() documentation:

返回的指针一直有效,直到… sqlite3_step() 或 sqlite3_reset() 或 sqlite3_finalize() 被调用。

您必须在完成声明之前提取文本。 或者,将语句对象保留在您的 DataObject 中。

此外,您必须检查所有函数的返回值是否有错误。

【讨论】:

感谢您的快速答复。我又调试了一遍。我认为我的问题不是指针的有效性。我将上面显示的 sqlite3_bind_text 函数之后的文本复制到 std::sting 中。此时: case SQLITE_TEXT: dataObj_.setText(reinterpret_cast(sqlite3_value_text(value)));休息; //end SQLITE_TEXT 抛出异常。我还尝试将“SQLITE_TRANSIENT”添加到绑定功能机器人中,但没有任何帮助。 您没有复制文本;您正在复制指向内部 sqlite3_value 对象的指针。 这里有同样的例外:sqlite3 *tmpDB; sqlite3_open_v2(":memory", &amp;tmpDB, SQLITE_OPEN_READWRITE, 0); sqlite3_stmt *tmpStmt = nullptr; sqlite3_prepare_v2(tmpDB, "SELECT ?;", 9, &amp;tmpStmt, nullptr); sqlite3_bind_text(tmpStmt, 0, "paul", 4, SQLITE_TRANSIENT); std::string s(reinterpret_cast&lt;const char*&gt; (sqlite3_value_text(sqlite3_column_value(tmpStmt, 0)))); 我的问题是,我不明白为什么该语句没有被执行?我必须怎么做才能毫无例外地获得有效的 std::string ?? 您必须执行查询(使用sqlite3_step())。再说一遍,您必须在完成语句之前获取字符串。 这就是我所做的。我更新了上面的问题以便更好地理解。每次调用 sqlite3_finalize() 之前都会抛出异常。接下来我不明白的是 sqlite3_step() = 21 (SQLITE_MISUSE) 的结果。任何想法都一定是错误的。也许我无法使用 sqlite。

以上是关于如何创建一个 sqlite3_value 以使用 gtest 进行测试的主要内容,如果未能解决你的问题,请参考以下文章

sqlite3_create_function 的使用

Android SQLite 加入自定义函数

如何以编程方式创建和使用 UIcollectionView?

如何使用 CSS 计算正确的变换以在 div 上以特定角度创建切角

Wix - 如何创建一个临时文件以在安装期间使用

如何使用 Swift 以编程方式创建 UILabel?