C++ Unit Test and Dependency Injection

TDD (test driven development) is widely adopted in modern development such as web development. Because it allows the developers to test the solution robustly in order to produce a more stable product.

Higher level programming languages like JavaScript and Ruby allows the developers to easily mock the functions and data to test the target specification. However, programming language like C++ is not designed for TDD. It will be more complex if you want to mock functions.

In order to adopt TDD, we need to write the function as small as possible, so that it can be easily mocked. As a result, the design of the object class needs to be testable. The methods that we are going to test needs to be publicly accessible.

Unlike JavaScript, C++ is prototype-based programming language. JavaScript can easily be mocked by overriding methods. In order to mock the C++ class, I have to use inheritance and mock the public methods. Example from my project Med.

class SnapshotScanTester : public SnapshotScan {
public:
  SnapshotScanTester() : SnapshotScan() {
    scannedValue = NULL;
  }
  virtual Bytes* getValueAsNewBytes(long pid, ScanType scanType) {
    Byte* data = bm.newByte(8);
    memset(data, 0, 8);
    data[0] = 20;
    Bytes* bytes = new Bytes(data, 8);
    return bytes;
  }
};

In order to mock the class SnapshotScan, I need to make the getValueAsNewBytes method as virtual public. Then using CxxTest framework to test with SnapshotScanTester.

Dependency Injection

I learnt the term Dependency Injection when I was developing with AngularJS project. By using dependency injection solution, we can create the services and inject to the client (object).

Med project is complex. The main feature of Med is to scan the memory of other process. Mocking other process is impractical for the test. In order to solve this, I refactored the code that involves PID (process ID) as the parameter, and write them into a service. For example,

class SnapshotScanService {
public:
  SnapshotScanService();
  virtual ~SnapshotScanService();

  virtual bool compareScan(SnapshotScan* scan, long pid, const ScanParser::OpType& opType, const ScanType& scanType);

  virtual void updateScannedValue(SnapshotScan* scan, long pid, const ScanType& scanType);
};

Before the refactoring, my code is doing something like

snapshotScan.compareScan(pid, opType, scanType);

By refactoring them into a service, then I can mock the service to produce any value for the testing.

In order to inject the service, I wrote the constructor that can accept the service,

class Snapshot {
public:
  Snapshot(SnapshotScanService* service = NULL);
  // ...
};

If service is not provided to the constructor, then default service will be instantiated. Therefore, if I want to mock the service, I just create a new class derived from SnapshotScanService. For example,

class SnapshotScanServiceTester : public SnapshotScanService {
public:
  SnapshotScanServiceTester() {}

  virtual bool compareScan(SnapshotScan*, long, const ScanParser::OpType&, const ScanType&) {
    return true;
  }
  virtual void updateScannedValue(SnapshotScan* scan, long pid, const ScanType& scanType) {
    Bytes* currentBytes = Bytes::create(20);
    currentBytes->getData()[0] = 60;
    scan->freeScannedValue();
    scan->setScannedValue(currentBytes);
  }
};

Then in the test,

    SnapshotScanService* service = new SnapshotScanServiceTester();
    SnapshotTester* snapshot = new SnapshotTester(service);

So, by using the dependency injection, I can finally test my class properly to make sure its functionality reliable. Without unit test, meaning I need to test my code by running thousands of time to create different situations. I am not genius, I don’t think that my code without proper test can function properly.

Unit testing and TDD

  1. I was thinking, WT* is the unit test?
  2. Why should I need unit test?
  3. Why should I spend my time to prepare the test cases?
  4. Why should I spend my time to become a tester instead of a developer?

For the first question, if searching online, what you get are just bunch of almost useless information. They can let you get high score in exam.

For the second question, instead of answering why, I will say, it depends. It depends on the language you are using and the framework that you are using. Let’s say you are working on C programming, there is less support of unit testing library. If you are working on Linux kernel development, unit test is difficult to implement. But, if you are developing web application, then unit testing and other testings are highly recommended. Why? Let me go to the next two answers beforehand.

For the 3rd and 4th questions, if you are not a tester, you no need spend your time specifically to prepare the test case. But, since you are a developer, surely you need to test your function during the development. Then, that is the time you prepare your test cases.

Why do you need “testing”?

Because of the advance of the web applications, the faster web development is needed. That is why there are so many MVC frameworks. But, faster development does not guarantee stable and valid products. In order to make it stable and valid (do what I want), you need to test it.

Since I was developing command-line tools, C/C++ libraries, scripting, desktop application using GUI widgets like GTK+/GTKMM, they are less likely to be tested as MVC. For example, if you are writing a linear function, y=mx+c, are you going to write a test case on it? Surely no. You can write a test case, but it is insignificant to see the advantage of unit testing. Because it is too simple. Nevertheless, if you insist to write the test cases, then you can include the test cases with non numerical input parameters, numerical input parameters, etc. However, since you are not a tester, but a developer, surely you can ignore this kind of testings.

In the case of web development, it is different. The reason is, because your web application involves so many things, MVC: model, view, and controller. It involves I/O, database, HTTP protocols, session, cookie, cache, email, HTML, JavaScript, CSS, etc. So, the question is, how are you going to test your product? Are you going to test everything? Yes, you should test everything. But test from login and click every links, enter various types of inputs, until logout, is just impracticable. So, that is why test-driven development (TDD) comes in. And it involves unit test and integration test. Unit test is the smallest unit testing. It is smallest, to make sure each unit works as expected individually. Only if the unit testing is passed, then we do the integration testing, which tests the units together.

Now, interesting part is writing the test cases. Once we write the test cases, then we can test these cases in automation. This is the part I like most. When you do the testing, it will go through all the test cases you have written. This means that, we can get the bugs when we do the development and testing. Since the web application involves so many components, we will break the functionality easily when we add some features. By adding new functions, we modify the old function, and accidentally break the old functions. Without using automated testing, we will prone to test the new functions only, because we “knew” that, old functions work.

So, using unit test and integration test, we can make sure that our product passes all the test cases. And we are confident that our product is not just being tested with the latest function, but the existing functions are still working.