Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is a good way to unit test private methods that are not suitable to be extracted as a new class?

Roughly speaking I have a class that implements a thread with only one public method run. That method goes into a loop, acting like a dispatcher to process network messages one by one; like the following:

class WorkerThread {

  public:

    void run() {
      while (!b_shutdown) {
        message = getNextMessage();
        switch(message.type) {
          case WRITE:
            write();
          case READ:
            read();
          // ...
          // more cases to handle
        }
      }
    }


  private:

    void write() { /* logic to test */ }
    void read() { /* logic to test */ }
    // more private methods with logic that needs testing

    // some member variables
};

So the main point is that I really don't want to

  1. Extract the private methods out to another class, because semantically they are part of this WorkerThread.
  2. Make the methods public, because they are not used anywhere outside the class.
  3. Skip the tests, because it is those methods that implements the main logic.

But how can the private methods be tested elegantly then?

Note:

  1. There might have to be a few more public methods to handle the start and termination of the thread, but that is not the concern here.
  2. I think this issue is not restricted to c++, so I tagged it with the more popular statically typed Java as well to get more attention :P
like image 624
wlnirvana Avatar asked Dec 07 '25 08:12

wlnirvana


2 Answers

Simply put: Don't. Test against the public interface and nothing else. Ultimately that's the only thing that matters.

If you want to see if it's working correctly, the read/write functions should be accessed via an IoInterface which can be mocked out for testing.

like image 193
mattideluxe Avatar answered Dec 08 '25 23:12

mattideluxe


Quoting the question:

I really don't want to extract the private methods out to another class, because semantically they are part of this WorkerThread.

And that is wrong. You are violating the Single Responsibility Principle.

Providing "multi threading support" and "doing actual work" are two different things. You should not enforce that "doing the actual work" is a private implementation detail.

In that sense: the rules that you put up for yourself actually caused you to write hard to test code. So, instead of "working around" the symptoms of a bad design - better step back and fix your design.

In other words:

  • rework your worker thread to be able to run any kind of work
  • separate concerns; and put "this kind of work" into a specialized class

By doing so, will not only improve your design, you will also make it much easier to test: because now, you can unit test "doing the actual work" without any multi-threading complexity. You simply assert that stuff works when done sequentially. Then you test the multi-thread part - on its own; without worrying about the actual work. And in the end, everything comes together nicely.

Instead of you worrying how to test internal implementation details. Which - you don't; the other answer is fully correct there!

like image 38
GhostCat Avatar answered Dec 09 '25 01:12

GhostCat