Mushfiqur Rahman Abir
Mushfiqur Rahman Abir

Reputation: 858

How to unit test a void function in C++

I am working on a hobby project mainly to learn cpp unit testing and database programming. However I am a little bit lost & confused about how should I write my code for proper testing. I tend to write a lot of void functions for my cpp projects. But now I can not figure out how should I test those functions. I have been succeeded in testing non-void functions cause they return something which can be easily tested against a value.

Ami I doing things in an unprofessional way? Should I avoid void functions as much as possible so that I can test those functions ? Or I am missing something ? For example how would I be able to test this function -

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);
  }

If Needed

Update

I have tried this one is this approach of testing the above method correct ?

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);
}

Upvotes: 0

Views: 4151

Answers (1)

Ulrich Eckhardt
Ulrich Eckhardt

Reputation: 17444

The problem is not so much in the function returning void. Think about how it signals errors and make sure all cases (success and failures) are tested, simple as that.

However, I don't see any error signalling at all there, apart from logging it. As a rule of thumb, logging should only be used for post-mortem research and the like. So, if logging completely fails, your program can still run correctly. That means, nothing internally depends on it and it is not a suitable error handling/signalling mechanism.

Now, there are basically three ways to signal errors:

  1. Return values. Typically used in C code and sometimes used in C++ as well. With void return, that's not an option, and that is probably the source of your question.
  2. Exceptions. You could throw std::runtime_error("DB connect failed"); and delegate handling it to the calling code.
  3. Side effects. You could store the connection state in your Database instance. For completeness, using a global errno is also possible, but not advisable.

In any case, all three ways can be exercised and verified in unit tests.

Upvotes: 2

Related Questions