【问题标题】:How to unit test a void function in C++如何在 C++ 中对 void 函数进行单元测试
【发布时间】:2021-08-02 14:56:48
【问题描述】:

我正在从事一个爱好项目,主要是学习 cpp 单元测试和数据库编程。但是,对于如何编写代码以进行正确测试,我有点迷茫和困惑。我倾向于为我的 cpp 项目编写很多 void 函数。但现在我不知道应该如何测试这些功能。我已经成功地测试了非 void 函数,因为它们返回的东西可以很容易地针对一个值进行测试。

阿米我做事不专业?我是否应该尽可能避免使用 void 函数以便测试这些函数?或者我错过了什么?例如,我将如何测试此功能 -

database.cpp

#include "database.hpp"

#include <sqlite3.h>

#include <iostream>

#include "spdlog/sinks/basic_file_sink.h"

// Creating the logging object
auto logger = spdlog::basic_logger_mt("appnotex", "../data/appnotexlog");

void Database::createDb(const char *dbname) {
  // Creating the database file
  sqlite3 *datadb;
  int status = sqlite3_open(dbname, &datadb);

  //    checking for errors
  if (status == SQLITE_OK) {
    logger->info("------------ New Session ----------");
    logger->info("Connected to Database Successfully");
  } else {
    std::string errorMessage = sqlite3_errmsg(datadb);
    logger->info("Error: " + errorMessage);
  }

如果需要

  • 我正在使用 Google Test 框架
  • 我的整个项目代码托管 - here

更新

我试过这个方法是不是测试上述方法正确?

databaseTest.cpp

TEST(DatabaseTest, createDbTest) {
  const char *dbfilename = "../data/test/data.db";
  const char *tbname = "DataTest";
  Database *db = new Database();

  std::ifstream dbfile("../data/test/data.db");
  bool ok = false;
  if (!dbfile.is_open())
    ok = false;
  else
    ok = true;

  EXPECT_TRUE(ok);
}

【问题讨论】:

  • 函数不仅有返回值,还有副作用。了解它们是什么并测试先决条件
  • 我建议您将测试重点放在您希望Database 展示的行为上,而不是其中的个别功能。测试单个功能是测试出错的一种方式。测试每个函数的方法可以使封装看起来像一个问题,而重构是可以避免的。有两件事显然不正确。
  • 基本上你的代码依赖于sqlite3(外部代码)。 Database::createDb 的功能是处理sqlite3 API 的调用。因此,要正确测试这一点,您必须提供依赖注入。这意味着您必须用可以转换为sqlite3 API 的内容替换对sqlite3 APi 的调用。使用 googletest 有 gmock 工具可以帮助进行测试。
  • 在生产代码之后编写测试也更难,当你以相反的方式进行时。当您首先编写测试时,您将专注于假设要测试而不是当前实现的行为。
  • 别人说的,不要执着于返回值,在某种意义上这就是实现细节。您无需测试返回例如 int 的函数是否真的返回 int ;),但您想测试行为

标签: c++ unit-testing testing googletest void


【解决方案1】:

问题不在于返回void 的函数。想想它是如何发出错误信号的,并确保所有案例(成功和失败)都经过测试,就这么简单。

但是,除了记录它之外,我根本看不到任何错误信号。根据经验,日志记录只能用于事后研究等。因此,如果日志记录完全失败,您的程序仍然可以正常运行。这意味着,没有任何内部依赖于它,并且它不是一个合适的错误处理/信号机制。

现在,基本上有三种方式来发出错误信号:

  1. 返回值。通常用于 C 代码,有时也用于 C++。使用void return,这不是一个选项,这可能是您问题的根源。
  2. 例外。您可以 throw std::runtime_error("DB connect failed"); 并将其处理委托给调用代码。
  3. 副作用。您可以将连接状态存储在您的 Database 实例中。为了完整起见,也可以使用全局 errno,但不建议这样做。

在任何情况下,这三种方式都可以在单元测试中进行练习和验证。

【讨论】:

  • 谢谢。你让它变得更容易和更清晰。从现在开始,我可能会使用 throw 关键字,并且我想我将不得不阅读更多关于信号的信息。
猜你喜欢
  • 2014-02-08
  • 1970-01-01
  • 2021-07-16
  • 1970-01-01
  • 2019-04-21
  • 1970-01-01
  • 1970-01-01
  • 2018-10-03
  • 2012-12-11
相关资源
最近更新 更多