单元测试之CppTest测试框架

1 背景

前面文章CppTest实战演示中讲述如何使用CppTest库。其主函数如下:

int main(int argc, char *argv[])
{
    Test::Suite mainSuite;
    Test::TextOutput output(Test::TextOutput::Verbose);

    mainSuite.add(std::unique_ptr<Test::Suite>(new SeesionSuite));
    mainSuite.run(output, true);

    return 0;
}

以上代码有一点不好,就是每增加一个测试Suite就需要在main函数中调用mainSuite.add增加用例。有没有办法测试Suite自动添加,不需要修改main函数。下面讲述的测试框架可以解决这个问题。

2 设计

首先设计类TestApp,该类是单例模式,可以添加测试Suite,其次AutoAddSuite是一模板类在其构造函数中自动添加测试Suite.
其类图如下:
类图

类定义如下:

class TestApp
{
    Test::Suite mainSuite_;
    TestApp();
public:
    static TestApp& Instance();

    void  addSuite(Test::Suite * suite);
    int run(int argc, char *argv[]);
};
#define theTestApp TestApp::Instance()

template<typename Suite>
class AutoAddSuite
{
    Suite* suite;
public:
    AutoAddSuite()
    : suite(new Suite())
    { 
        theTestApp.addSuite(suite);
    }
};
#define ADD_SUITE(Type) AutoAddSuite<Type>  add##Type

说明:

  • TestApp类型是单例类,提高增加Suite接口和run接口
  • AutoAddSuite是一个自动添加Suite的模板类型
  • 宏ADD_SUITE定义了AutoAddSuite对象,用于自动添加。

3 实现

#include "testapp.h"

#include <iostream>
#include <cstring>
#include <cstdio>

namespace
{
void usage()
{
    std::cout << "usage: test [MODE]\n"
         << "where MODE may be one of:\n"
         << "  --compiler\n"
         << "  --html\n"
         << "  --text-terse (default)\n"
         << "  --text-verbose\n";
    exit(0);
}

std::unique_ptr<Test::Output> cmdline(int argc, char* argv[])
{
    if (argc > 2)
        usage(); // will not return

    Test::Output* output = 0;

    if (argc == 1)
        output = new Test::TextOutput(Test::TextOutput::Verbose);
    else
    {
        const char* arg = argv[1];
        if (strcmp(arg, "--compiler") == 0)
            output = new Test::CompilerOutput;
        else if (strcmp(arg, "--html") == 0)
            output =  new Test::HtmlOutput;
        else if (strcmp(arg, "--text-terse") == 0)
            output = new Test::TextOutput(Test::TextOutput::Terse);
        else if (strcmp(arg, "--text-verbose") == 0)
            output = new Test::TextOutput(Test::TextOutput::Verbose);
        else
        {
            std::cout << "invalid commandline argument: " << arg << std::endl;
            usage(); // will not return
        }
    }

    return std::unique_ptr<Test::Output>(output);
}
}

TestApp & TestApp::Instance()
{
    static TestApp theApp;
    return theApp;
}

TestApp::TestApp()
{}

void TestApp::addSuite(Test::Suite * suite)
{
    mainSuite_.add(std::unique_ptr<Test::Suite>(suite));
}

int TestApp::run(int argc, char *argv[])
{
    try
    {
        std::unique_ptr<Test::Output> output(cmdline(argc, argv));
        mainSuite_.run(*output, true);

        Test::HtmlOutput* const html = dynamic_cast<Test::HtmlOutput*>(output.get());
        if (html)
            html->generate(std::cout, true, argv[0]);
    }
    catch (...)
    {
        std::cout << "unexpected exception encountered\n";
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

说明:

  • Instance 返回一个单例引用
  • addSuite 增加Suite到mainSuite_
  • run
    • 首先根据命令行返回Test::Output
    • 然后调用mainSuite_运行测试用例
    • 最后如果类型是Output是Test::HtmlOutput类型,则将结果输出到标准输出std::cout.

4 使用

4.1 主函数

#include "testapp.h"

int main(int argc, char *argv[])
{
    try
    {
        theTestApp.run(argc, argv);
    }
    catch(const std::exception& e)
    {
        std::cerr << e.what() << '\n';
    }
    return 0;
}

主函数很简单,不再详述。

4.2 测试用例

这里使用C++标准库中std::mutex作为测试示例.

4.2.1 定义

#ifndef MUTEX_TEST_H
#define MUTEX_TEST_H
#include <cpptest/cpptest.h>

class MutexSuite : public Test::Suite
{
public:
    MutexSuite()
    {
        TEST_ADD(MutexSuite::construct)
        TEST_ADD(MutexSuite::lock)
        TEST_ADD(MutexSuite::try_lock)
        TEST_ADD(MutexSuite::unlock)
    }

    void construct();
    void lock();
    void try_lock();
    void unlock();
};
#endif

说明:

  • cpptest库标准使用,不再详述。

4.2.2 实现

#include "mutex_test.h"
#include "testapp.h"

#include <thread>
#include <mutex>

ADD_SUITE(MutexSuite);

void addCount(std::mutex & mutex, int & count)
{
    mutex.lock();
    count++;
    mutex.unlock();
}

void MutexSuite::construct()
{
    std::mutex muxtex;
    int count = 0;
    std::thread threads[10];
    for(int i = 0; i < 10; i++)
        threads[i] = std::thread(addCount, std::ref(muxtex), std::ref(count));
    for(auto &thread : threads)
        thread.join();
    TEST_ASSERT_EQUALS(10, count)   
}

void MutexSuite::lock()
{
    std::mutex muxtex;
    int count = 10;
    std::thread threads[10];
    for(int i = 0; i < 10; i++)
        threads[i] = std::thread(addCount, std::ref(muxtex), std::ref(count));
    for(auto &thread : threads)
        thread.join();
    TEST_ASSERT_EQUALS(20, count)   
}

struct Function
{
    volatile int counter = 0;
    void add_10k_count(std::mutex & muxtex)
    {
        for(int i = 0; i < 10000; i++)
        {
            if(muxtex.try_lock())
            {
                ++counter;
                muxtex.unlock();
            }
        }
    }
};

void MutexSuite::try_lock()
{
    std::mutex muxtex;
    Function function;
    std::thread threads[10];
    for(int i = 0; i < 10; i++)
        threads[i] = std::thread(&Function::add_10k_count, std::ref(function), std::ref(muxtex));
    for(auto &thread : threads)
        thread.join();
    TEST_ASSERT_EQUALS(true, function.counter < (10 * 10000))
    std::cerr << "function.counter: " << function.counter << std::endl;
}

void MutexSuite::unlock()
{
    std::mutex muxtex;
    int count = 20;
    std::thread threads[10];
    for(int i = 0; i < 10; i++)
        threads[i] = std::thread(addCount, std::ref(muxtex), std::ref(count));
    for(auto &thread : threads)
        thread.join();
    TEST_ASSERT_EQUALS(30, count)   
}

说明:

  • 重点说明下文件头部宏ADD_SUITE的使用,如下所示传递给ADD_SUITE的参数正是MutexSuite,通过该定义,将MutexSuite自动添加到TestApp实例中,不需要修改main函数.
ADD_SUITE(MutexSuite);

4.3 运行

$ testapp --html

说明:

  • testapp是编译出的可执行文件
  • 参数–html 说明测试报告按网页格式输出.
  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,让我为您详细介绍如何使用cpptest来为未完全定义的函数编写桩函数,并进行单元测试。 步骤1:创建stubdemo项目 首先,您需要创建一个名为“stubdemo”的新项目。您可以使用任何您喜欢的集成开发环境(IDE)或编辑器,例如Visual Studio、Eclipse或Sublime Text。 步骤2:在main.c中设计不完整的函数test 在stubdemo项目中的main.c文件中,您需要编写一个不完整的函数test。例如,您可以编写以下代码: ```C int test(int a, int b) { // 未完全定义的函数 } ``` 步骤3:进行单元测试 接下来,您需要为您的test函数编写单元测试。在这里,我们将使用cpptest作为示例。 在您的stubdemo项目中,创建一个名为“test.cpp”的新文件,并在其中编写您的单元测试代码。例如,您可以编写以下代码: ```C++ #include "cpptest.h" #include "main.c" void TestFunction(TestResult& result) { int actual = test(1, 2); int expected = 3; TEST_ASSERT(actual == expected); } int main() { TestSuite testSuite("TestSuite"); testSuite.addTest(new TestCaller<TestResult>("TestFunction", TestFunction)); TestResult result; testSuite.run(result); return result.getFailureCount(); } ``` 在这个例子中,我们假设test函数应该返回两个整数的和,因此我们期望将1和2作为参数传递给test函数时返回3。在TestFunction中,我们调用test函数,然后使用TEST_ASSERT宏来比较test函数的实际返回值和期望值。 步骤4:在桩函数视图下显示未完全定义的test 在进行单元测试之前,您可以在cpptest的“桩函数视图”中查看test函数的定义。在这里,您应该看到test函数被标记为“未定义”。 步骤5:自动生成桩函数 接下来,您可以使用cpptest的“自动生成桩函数”功能来生成test函数的桩函数。要使用此功能,请右键单击test函数,然后选择“自动生成桩函数”。 在生成桩函数之后,您应该可以在“桩函数视图”中看到test函数的桩函数定义。在这个例子中,我们将test函数的桩函数定义为返回其两个整数参数的和。 ```C int test(int a, int b) { return a + b; } ``` 步骤6:完成单元测试 现在,您可以重新运行单元测试,并确保test函数按照您的期望返回正确的结果。在这个例子中,我们期望将1和2作为参数传递给test函数时返回3。如果单元测试通过,则说明您已经成功地为未完全定义的函数编写了一个桩函数,并且可以在没有实际函数实现的情况下进行单元测试。 总结 在这个过程中,您学习了如何为未完全定义的函数编写桩函数,并且可以在没有实际函数实现的情况下进行单元测试。这使您可以在开发过程的早期进行单元测试,并确保您的代码在实现完整的功能之前就能够达到预期的结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

flysnow010

你的鼓励就是我最大的创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值