I am using QThreadPool
to run tasks in my application in parallel. The tasks take the thread pool as an argument, so they can start new tasks. How can I write unit tests for the tasks and assert that the correct next tasks are started?
class MyTask: public QRunnable {
public:
virtual void run() {
m_threadPool.start(/*another task*/);
m_threadPool.start(/*a third task*/);
}
private:
QThreadPool &m_threadPool;
};
I would like to test MyTask
:
QThreadPool threadPool;
threadPool.start(new MyTask(threadPool));
threadPool.waitForDone();
// Assert that another task and a third task is started.
I tried extending QThreadPool
and log started tasks:
class MyThreadPool : public QThreadPool {
public:
virtual void start(QRunnable *runnable, int priority = 0) {
m_Queue.enqueue(runnable);
// The task is not started, so I can manually start each task and test the outcome.
}
QQueue<QRunnable *> queue() const { return m_queue; }
private:
QQueue<QRunnable *> m_queue;
};
MyThreadPool threadPool;
threadPool.start(new MyTask(threadPool));
threadPool.waitForDone();
QQueue<QRunnable *> Queue({ /*another task and a third task*/ });
Assert::IsEquavalent(threadPool.queue(), Queue);
But this does not work, since QThreadPool::start()
is not virtual. What is the best approach for writing my test?
Regarding the problems about the fact that QThreadPool::start()
is not a virtual function, you can do something like this:
Instead of overriding a function, you can use your subclass in MyTask and use a different function that will call run. Something like this:
class MyThreadPool : public QThreadPool {
public:
void enqueue_and_run(QRunnable *runnable, int priority = 0) {
m_Queue.enqueue(runnable);
QThreadPool::start(runnable, priority);
}
const QQueue<QRunnable *>& queue() const {
return m_queue;
}
private:
QQueue<QRunnable *> m_queue;
};
class MyTask: public QRunnable {
public:
virtual void run() {
m_threadPool.enqueue_and_run(/*another task*/);
m_threadPool.enqueue_and_run(/*a third task*/);
}
private:
MyThreadPool &m_threadPool;
};
THen run the same test code:
MyThreadPool threadPool;
threadPool.enqueue_and_run(new MyTask(threadPool));
threadPool.waitForDone();
QQueue<QRunnable *> Queue({ /*another task and a third task*/ });
Assert::IsEquavalent(threadPool.queue(), Queue);
It is not the most elegant way to do so, but it makes clear your intentions.
As an alternative, if you want to keep a common interface, you can use a basic function overloading:
template <class TPool>
void start(TPool* pool, QRunnable *runnable, int priority = 0) {
pool->start(runnable, priority);
}
void start(MyThreadPool* pool, QRunnable *runnable, int priority = 0) {
pool->enqueue_and_run(pool, runnable, priority);
}
Then your test code will work pretty similar to the original one:
MyThreadPool threadPool;
start(threadPool, new MyTask(threadPool));
threadPool.waitForDone();
// ... rest of the code