Qt signal slots and gmock

6,813 views
Skip to first unread message

Paul Hansen

unread,
Feb 27, 2012, 4:44:59 AM2/27/12
to Google C++ Mocking Framework
Hi

I have a Qt "signal-slot connection".
I want to test the class with the signal
The class with the slot is mocked/stubbed.
Now I would like to verify that when I emit signal, the slot is
called.
I wonder if it is possible to use gmock to verify this.

// class Tx contains the signal, which is protected so I inherit
// to get access to it
class Test : public ::testing::Test, public Tx
{ public:
MockRx *rx;
void SetUp()
{ rx = new MockRx;
rx->rxsignal(); // Just to see it compiles
rx->gmock_rxsignal(); // Compiles, gmock generated function
QObject::connect(this, SIGNAL(txsignal(), rx, SLOT(rxsignal()));
}
void TearDown() { delete rx; }
};
TEST_F(Test, Signal)
{
EXPECT_CALL(*rx, rxsignal()).Times(1).WillOnce(Return());
this->txsignal();
}
class MockRx : public QWidget
{ Q_OBJECT
public:
MOCK_METHOD0(rxsignal, void());
};

When running, I get the error:
QObject::connect: No such slot as RxMock::rxsignal()
If I remove Q_OBJECT in class MockRx, I get: No such slot as
QWidget::rxsignal()

I also tried to QObject::conntect(... SLOT(gmock_rxsignal()));
which gives No such slot as RxMock::rxsignal()

If I replace the MockRx class with a class Rx with rxsignal()
everything works.

I wonder why QObject::connect cannot find RxText::rxsignal() when
- it works if I replace with a regular class Rx instead of class
MockRx
- rx->rxsignal() can be called
Is gmock doing something under the hood that prevents this?

Hope someone can help, thanks a lot
Paul

Greg Miller

unread,
Feb 27, 2012, 10:26:26 AM2/27/12
to Paul Hansen, Google C++ Mocking Framework
I don't know anything about Qt or its signals and slots. But here are a few questions/observations:

- You shouldn't be explicitly calling gmock functions like gmock_rxsignal().
- Your error message references "RxMock", but the class is "MockRx". Am I misunderstanding this or is there a typo somewhere?
- Is the rxsignal() method you're mocking virtual?

Perhaps someone else who knows more about Qt can be more help.

Greg

Janne Hakonen

unread,
Feb 27, 2012, 12:49:03 PM2/27/12
to Google C++ Mocking Framework
Hmm, I usually haven't had need for unit testing signal-slot
connections because I check with assert that the connect succeeds:
bool ok;
...
ok = QObject::connect( ... );
Q_ASSERT( ok );


> class Test : public ::testing::Test, public Tx
This might not be necessary, as you can use QT's meta object to call
the signal:
Tx tx;
QMetaObject::invokeMethod( &tx, "txsignal" );


> I wonder why QObject::connect cannot find RxText::rxsignal() when
It doesn't work because the moc-generator doesn't see that
MOCK_METHOD0(rxsignal, void()) as a slot, so it hasn't generated any
of the slot code for that method.
You could try this instead:
class MockRx : public Rx
{
public:
MOCK_METHOD0(rxsignal, void());

};
In the real Rx-class, the rxsignal method must be declared as a
virtual slot.

-Janne

On Feb 27, 5:26 pm, Greg Miller <j...@google.com> wrote:
> I don't know anything about Qt or its signals and slots. But here are a few
> questions/observations:
>
> - You shouldn't be explicitly calling gmock functions like gmock_rxsignal().
> - Your error message references "RxMock", but the class is "MockRx". Am I
> misunderstanding this or is there a typo somewhere?
> - Is the rxsignal() method you're mocking virtual?
>
> Perhaps someone else who knows more about Qt can be more help.
>
> Greg
>

Slavik81

unread,
Feb 27, 2012, 3:29:06 PM2/27/12
to Google C++ Mocking Framework
You didn't declare rxsignal as a slot. You made it a regular public
function, hence why it says no slot exists by that name.

However, the Qt moc cannot parse the mock macro. So you cannot
directly declare MOCK_METHODs as slots.

class AbstractMockRx : public QWidget
{ Q_OBJECT
public slots:
virtual void rxsignal()=0;
};

class MockRx : public AbstractMockRx
{ Q_OBJECT
public:
MOCK_METHOD0(rxsignal, void());
};

That's the best I can do for you.

Slavik81

unread,
Feb 27, 2012, 3:49:48 PM2/27/12
to googl...@googlegroups.com
I didn't notice at first, but Janne Harkonen suggested my exact solution.
But, if anyone knows of a solution to this problem that doesn't require so much duplication, that would be wonderful. I've run into this problem personally and can't stand declaring my slots twice for objects that do not need an interface.

SSJ

unread,
Mar 6, 2012, 9:49:52 AM3/6/12
to Google C++ Mocking Framework
Interesting timing :) If I'm understanding correctly, then I just
started work on a small helper class + macros to help with this
situation.

It's too early to make any pronouncements, but my prototype test case
I'm working on currently works so far, although I haven't tried
anything complex, yet, so everything may well come to naught ;)

The class I'm testing is called simply "Downloader", and I'm EXPECTing
it to emit its "completed(const QByteArray& downloadedData)" with the
correct downloadedData.

To declare the mock:

class DownloaderSignalSpy : public QSignalMock
{
public:
MOCK_METHOD1(completed, void(const QByteArray&)); // i.e.,
this ill be called every time the spied on object
// emits the
corresponding signal, completed(const QByteArray&).

REGISTER_FOR_SIGNALS(DownloaderSignalSpy)
{
SIGNAL_1(completed, (const QByteArray&));
}
};

The syntax has more duplication than I would like (I would have
preferred a single "MOCK_SLOTx" for each slot instead of
"MOCK_METHODx" followed by a further SIGNAL_x, duplicating the slot
name and arguments), but I've wracked my brains trying to think of a
solution and can't, yet (I'm beginning to think it's impossible). The
basic idea is to give DownloaderSignalSpy a QObject, q say, and then
for each signal that q can emit and that we're interested in, add a
MOCK_METHOD with the signal's name and args, then register our
interest using SIGNAL_x yadda yadda in the (mandatory)
REGISTER_FOR_SIGNALS block. All signals registered are then, when q
emits them, routed to the corresponding MOCK_METHOD using some of the
techniques described in

http://doc.qt.nokia.com/qq/qq16-dynamicqobject.html

plus a bunch of ugly macro-hackery :)

Setting expectations is as expected:

Downloader downloader;
DownloaderSignalSpy downloaderSignalSpy(&downloader);

EXPECT_CALL(downloaderSignalSpy, completed(correctDownloadedData));

<start off the downloader, etc>

Note in particular that MOC is not required to be run on
DownloaderSignalSpy!

Is this the kind of thing you were after? I was about to ask the group
if there was a way to query whether a mock's expectations are
currently satisfied *without* clearing them (which rules out the
otherwise-ideal "Mock::VerifyAndClearExpectations") so that I can
implement a "hang around for a timeout to allow any EXPECTed signals
to come in" which will make it usable - at the moment, you can only
either a) not wait for asynchronous signals, potentially leading to
false failures if the signals aren't emitted by the time the mock is
destroyed or b) *always* wait the full timeout, which is too slow :/

PS Apologies if this message came through twice.
Message has been deleted

SSJ

unread,
Mar 7, 2012, 3:43:28 AM3/7/12
to Google C++ Mocking Framework
Inspirations strikes! I now have a working (with the simple cases I've
tested with, at least) QSignalMock2, which allows the much less noisy
and redundant syntax:

class DownloaderSignalSpy2 : public QSignalMock2
{
public:
BEGIN_MOCK_SIGNALS(DownloaderSignalSpy2);

MOCK_SIGNAL_1(completed, (const QByteArray&));
MOCK_SIGNAL_3(dataDownloaded(const QByteArray&, int, int));
// ... other signals ...
};

That's pretty much all there is too it - DownloaderSignalSpy2 does not
need to have moc run on it, and most of the implementation is tucked
away in qsignalspy.cpp, where it can be pre-compiled. The drawback is
that, during construction of any class deriving from QSignalMock2,
some static data of QSignalMock2 is manipulated, so constructing two
such objects at the same time would be undefined. Since mocks are
usually created in tests, and Google Test runs all it's tests in a
single thread, this shouldn't be a problem in practice, though it
still makes me a little queasy.

I'll post the implementations of QSignalSpy2 + supporting macros if
anyone is interested (and if I manage to tidy it up a bit!).

SSJ

unread,
Mar 7, 2012, 7:07:22 AM3/7/12
to Google C++ Mocking Framework
Here's what I have so far:

http://www.etotheipiplusone.com/projects/qsignalmock/qsignalmock.cpp
http://www.etotheipiplusone.com/projects/qsignalmock/qsignalmock.h

There are some compile-time dependencies on Boost, but these could
probably be removed with some copy-n-paste-ing.

Hope someone finds it useful - I haven't added an explicit license to
the source files, but consider them public domain!
Reply all
Reply to author
Forward
0 new messages