6

I'm writing unit tests for my app, and now I've stumbled on a class in which I should test private methods. This could be result of poor design of particular class, but I must do it. Is there any way in Qt to call private methods, maybe using QMetaObject or something similar ?

For unit testing I am using QTestLib framework.

xx77aBs
  • 4,678
  • 9
  • 53
  • 77
  • 1
    Just to comment that normally, only public methods are to be unittested. Private methods are an implementation detail: http://lostechies.com/chadmyers/2008/11/21/do-not-test-private-methods/ – kroonwijk Sep 12 '11 at 21:31
  • kroonwijk: Thanks for the comment :) I know that, but this class is inheriting QThread, so only public methods are constructor and start(). And things that tread is doing are private methods. Now when I think about it, I should've done something different ... But there it is. My question still stands :) – xx77aBs Sep 12 '11 at 21:41
  • 1
    Ideally, you shouldn't subclass QThread (http://stackoverflow.com/questions/3911086/qthread-threaded-rly/3911440#3911440). Encapsulate your processing logic in a QObject subclass, create an instance of it and move it to an instance of QThread. This might help with your unit testing too. – Arnold Spence Sep 12 '11 at 22:22
  • Thank you ;) In future I'll use that methodology when using threads ... I think that I've read that I should subclass QThread in some book. Also, QT Documentation says "To create your own threads, subclass QThread and reimplement run()." – xx77aBs Sep 13 '11 at 11:09
  • Yes, the documentation for Qt does suggest that and there is a very slow movement inside Trolltech to get it removed or at least updated :) – Arnold Spence Sep 13 '11 at 12:13
  • Here is a better answer: https://stackoverflow.com/questions/14186245/unit-testing-c-how-to-test-private-members – LinWM Jan 08 '19 at 02:02

5 Answers5

6

The proper (read annoying) answer is that you should not be testing private methods, those are implementation details ;-).

OTOH -- have you thought about conditionally declaring them protected/private depending on whether you are in testing or no and then extending? I've used that to get me out of a similar pinch in the past.

#ifdef TESTING
// or maybe even public!
#define ACCESS protected
#else
#define ACCESS private
#endif

/* your class here */
class Foo {
ACCESS
    int fooeyness;
}

// or better yet, place this in a different file!
#ifdef TESTING
/*
    class which extends the tested class which has public accessors
*/
#endif
cwallenpoole
  • 79,954
  • 26
  • 128
  • 166
  • That is interesting way to resolve the problem :) I am still looking for another (qt specific) way to access private methods. But if I don't find it, I'll use this :) Thanks ;) – xx77aBs Sep 12 '11 at 21:45
  • 3
    Or even dirtier: `#define private public` before you `#include" the header. – larsmoa Sep 13 '11 at 12:05
  • @larsm hahahah that scares me a little – xx77aBs Sep 13 '11 at 15:00
  • 3
    @larsm ... That is *still* evil three years later. Just thought I would point that out. Seriously. It makes me cringe just thinking about that. – cwallenpoole Aug 20 '14 at 02:24
2

Solution 1, quick and easy: make the test class(es) a friend of the public one.

class Foo {
   // ...
private:
   friend class FooTest;
};

This way your FooTest class can access all members of the public class. However, this way you need to modify the original class every time you want to access private data from a different test, and you leak information about the tests in the public API, and you possibly open up for class naming conflicts (what if there's /another/ FooTest class around?), and so on.


Solution 2, aka properly done: don't put private methods in the public class, but make a private class with public methods.

class Foo {
    // 
private:
    friend class FooPrivate;
    FooPrivate *d;
};

FooPrivate gets declared in its own header, which may not be installed, or stay in a include-privates/ subdirectory, or whatever -- i.e. it stays out of the way for normal usage. The public class stays clean this way.

class FooPrivate {
public:
    // only public stuff in here;
    // and especially this:
    static FooPrivate *get(Foo *f) { return f->d; }
};

The test then includes the private header and calls FooPrivate::get(fooObj) to get the private class instance and then happily uses it.

peppe
  • 21,934
  • 4
  • 55
  • 70
  • Nice idea, but if `FooPrivate` isn't installed the program won't be able to compile. How about using Solution 1 but wrapping it in IFDEFs that are only enabled when tests are running? – DBedrenko Jun 22 '16 at 07:47
  • 1
    Sure. But as I said, FooPrivate can be installed in some `include/private` subdirectory. Or, it can be installed only if one runs configure saying to enable tests... – peppe Jun 22 '16 at 07:52
  • I see what you mean now, but I'm not sure how that's an advantage over the very similar (and easier method) of wrapping `friend class TestFoo` in an `IFDEF` that is defined if project is configured to enable tests. – DBedrenko Jun 22 '16 at 08:12
2

I disagree with the "private members are implementation details" mentality, in my mind that roughly translates to "test only part of your code".

I can related to "units are units" argument, but why not try to cover as much of your code with tests as possible, even inside units? Aka. giving your units a thorough rectal examination.

And with this image in mind, one approach that I have been using frequently that is not mentioned in the other answers is to do the following:

  1. Always declare members you want to test as protected in stead of private in your code.
  2. Subclass your class in the testcode and simply make the selectors you need or simply write the test-code directly as members in that sub-class's implementation.

NOTE: You have to be careful with classes that rely on complex instantiation patterns to ensure that you construct them correctly (read: call the original class' constructor from the subclass ctor).

Mr. Developerdude
  • 9,118
  • 10
  • 57
  • 95
1

I have found more convenient way to do this. First, all private methods should be private slots. Then you create an instance of the class:

Foo a;

Then we can use QMetaObject::invokeMethod to call any slot that method has (public or private). So if we want to call method Test, we can do it like this:

QMetaObject::invokeMethod(&a, "Test", Qt::DirectConnection);

Also, we can have return value and send arguments ... Actually, everything is answered here: http://doc.qt.nokia.com/stable/qmetaobject.html#invokeMethod

xx77aBs
  • 4,678
  • 9
  • 53
  • 77
0

I've once read that Unit Test should test the class public interface, and not the protected/private stuff.

Your class should just behave right from outside. If implementation strategy changes, your Unit Test class is still the same.

nyarlathotep108
  • 5,275
  • 2
  • 26
  • 64