使用 QThreadPool 并行运行单元测试任务

Posted

技术标签:

【中文标题】使用 QThreadPool 并行运行单元测试任务【英文标题】:Unit test tasks run in parallel using QThreadPool 【发布时间】:2019-03-25 15:12:35 【问题描述】:

我正在使用QThreadPool 在我的应用程序中并行运行任务。这些任务将线程池作为参数,因此它们可以启动新任务。如何为任务编写单元测试并断言正确的下一个任务已启动?

class MyTask: public QRunnable 
public:
    virtual void run() 
        m_threadPool.start(/*another task*/);
        m_threadPool.start(/*a third task*/);
    
private:
    QThreadPool &m_threadPool;
;

我想测试MyTask:

QThreadPool threadPool;
threadPool.start(new MyTask(threadPool));
threadPool.waitForDone();

// Assert that another task and a third task is started.

我尝试扩展 QThreadPool 并记录启动的任务:

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);

但这不起作用,因为QThreadPool::start() 不是虚拟的。编写测试的最佳方法是什么?

【问题讨论】:

【参考方案1】:

关于QThreadPool::start()不是虚函数的问题,你可以这样做:

您可以在 MyTask 中使用您的子类并使用另一个调用 run 的函数,而不是覆盖函数。像这样的:

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;
;

然后运行相同的测试代码:

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);

这不是最优雅的方式,但它清楚地表明了你的意图。

作为替代方案,如果你想保留一个通用接口,你可以使用一个基本的函数重载:

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);
  

那么您的测试代码将与原始代码非常相似:

MyThreadPool threadPool;
start(threadPool, new MyTask(threadPool));
threadPool.waitForDone();
// ... rest of the code

【讨论】:

这会降低性能,因为MyTask 旨在用于生产。 为什么你认为它会降低性能?具有多态性的原始实现也需要调用基类实现(如果可能的话)。

以上是关于使用 QThreadPool 并行运行单元测试任务的主要内容,如果未能解决你的问题,请参考以下文章

使用 Visual Studio 测试任务并行运行测试失败

如何并行运行单元测试(MSTest)?

Java 单元测试 - 并行化 + 多线程 + 无限次

多线程之任务

在 QThreadPool 中执行槽

受控并行任务执行使用什么