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.

Advertisements

One thought on “C++ Unit Test and Dependency Injection

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s