美文网首页
测试框架是干什么的?

测试框架是干什么的?

作者: 东方胖 | 来源:发表于2021-12-08 20:40 被阅读0次

有一段时间,我负责一个语音通讯的SDK的集成自动化测试。

有别于单元测试的小粒度的测试,这种类型的测试一个SDK的对外接口可能涉及几十甚至上百个个函数的联动运行,但这不是这个级别的测试关心的事情。我只需要针对大约几十个对外的api进行调用,运行,验证返回值就行。

当时没有做过多的思虑,以为测试运行,断言和报告是比较容易的事情,我应该把精力放在跨平台,测试协作机,异步接口测试和环境构建这些问题上面。这些问题自然也是很重要,不过我可能低估了断言,报告驱动这些看起来很初级的东西。

在写代码过程中,我慢慢发现我写了很多的if-else
比如

HHErrorCode code = engine->setUserRole(USER_TALKER_FREE);
if (code == HHSuccess) {
     //输出一个Pass标记汇总到一个数据容器中,以便最后被测试报告收集
} else {
    //测试失败,我要打印一些信心,基本的比如,实际返回的错误码是XXX,而预期的错误码是YYY
}

//测试另一个参数
HHErrorCode code2 = engine->setUserRole(USER_TALKER_MIC);
.... 
//这里校验结果仍然需要一堆if-else ....

于是我自然的想到用宏把这些长得有点像的代码进行替换啦。
最后我的测试代码长得像这样:

EXPECT_EQ("设置用户说话时的角色", "setUserRole", code, HH_SUCCESS);

比if-else简洁了很多,出错时也会按照固定范式打印出一些错误信息,但是还是有些毛病,

  • 参数太多,这种宏压根不敢给别人用,你得解释很多,第一个参数是什么,第二个参数是什么,第三个参数是什么吗...
  • 看看我的"宏"是怎么实现的,其实不是真正的宏,是一个函数,前面说是宏是为了对应主流的测试框架,因为这里做的事情和框架的断言宏是类似的。

这个断言函数做了很多,如果要适用于更多场景,它有很大的问题
首先,它假设了expect和actual都是T类型,并且可以用 == 号比较。
我们知道,这未必。等号还可能存在一些陷阱。因此使用时比较多加小心,万一数据类型不能比较或者比较的不是值而是指针怎么办?我们必须把返回的结果“值化”,把它弄成可以比较的东西才可以用这个函数

template <class T>
static void EXPECT_EQ(const std::string&casename, const std::string & suitname,
                      T actual, T expect,
                      const std::string& comment=std::string())
{
    //std::lock_guard<std::mutex> lck(mtx, std::adopt_lock);
    mtx.lock();
    std::string message;
    std::stringstream ss;
    ss << actual;
    std::string actual_str = ss.str();
    ss.str("");
    ss << expect;
    std::string expect_str = ss.str();
    std::string placehold(' ', 8);

    if (casename.length() < 32) {
            for (std::string::size_type i = 0; i < casename.size(); ++i){
                placehold[i] = casename[i];
            }
    }

    std::string result;
    if (actual == expect) {
        result = "\t"
        "[        <font color=\"green\">OK</font>           ]";
    } else {
        result = cocos2d::StringUtils::format("\t"
                                              "[        <font color=\"red\">Failed</font>        ]"
                                              " ======= Expect is %s"
                                              ", But actual is %s", expect_str.c_str(), actual_str.c_str());
    }

    std::string row = cocos2d::StringUtils::format("<tr>"
                                                   "<td>%d</td>"
                                                   "<td>%s</td>"
                                                   "<td>%s</td>"
                                                   "<td>%s</td>"
                                                   "<td>%s</td>"
                                                   "</tr>", CaseNum++, suitname.c_str(),
                                                   casename.c_str(), result.c_str(),
                                                   comment.c_str());

    results.push_back(row);
    mtx.unlock();
    write_a_message(row);
}
  • 此外我做了很多格式化,最终都是为了报告
    以上的实现非常ugly,时隔多年再次看到简直是不忍卒睹。
    但是通过这个例子我们大概知道了,我们在写测试用例的时候,需要干很多“通用”的事情。

比如,上面的例子中,断言,如果你重新写断言函数或者C++的宏,显然你需要考虑很多:

  • 基本的判断,如预期值和实际值是否相等
  • 如果是对象地址判断呢,也许需要另一个函数
  • 布尔值字符串,像上面我写的那个断言函数,在判断const char*类型的时候会发生车祸(为什么?)为此,我又写了这个函数予以矫正
static void EXPECT_EQ_STR(const std::string&casename,
                          const std::string& suitname,
                          const char* actual,
                          const char* expect,
                          const std::string& comment = "") {
    EXPECT_EQ(casename, suitname, std::string(actual), std::string(expect), comment);
}

这样的东西显然不够精简

  • 测试报告,我们需要将每条测试结果写到报告中,最终呈现给哟用户进行及时反馈。报告的内容稍微想想,其实也有许多的内容,测试耗时,错误信息,执行时间戳,用例归属人,以及测试用例的信息树(所属的suit,模块等等)
  • 测试驱动和用例管理
  • 测试前置和后置

再看我的测试用例的某一个类,这个类包含一组测试的初始化,如事件注册,组件的初始化。然后将每条用例放到private控制的标号底下,在公共接口runtests中运行所有用例。
当然,如果要增加删减用例的话,不得不修改这个类,在private:下面增加一个函数,然后把这个函数加到runTests函数下面.... 这不是template模板方法的design pattern,虽然有点像,但它不是,毕竟每个用例不是virtual,我也无法预估会有多少用例不断被加进来。

class TalkCasesController : public TalkCallbackWrapper
{
public:
    void init();
    TalkCasesController();
    ~TalkCasesController();
    void startTest();
    void onEvent(const HHEvent event, const HHErrorCode error,
                 const char *channel, const char * param) override;
    void onPcmData(int channelNum,
              int samplingRateHz,
              int bytesPerSample,
              void* data,
              int dataSizeInByte) override;
    virtual void onRequestRestAPI( int requestID,
                                  const HHErrorCode &errorCode,
                                  const char* strQuery,
                                  const char*  strResult ) override;

    virtual void onBroadcast(const HHBroadcast bc, const char* channel, const char* param1, const char* param2, const char* strContent) override ;
  // ... 此处还有很多onXXXX的虚回调函数的实现


    void runTests();
    void actionInputData();
    void sendemail();
    std::string getRobotUser() {
        return m_robotuser;
    }

private:
    void actionUninit();
    void actionInit();
    void actionJoinSingleRoom();
    void actionLeaveSingleChannel();
    void actionJoinMultiRoom();
    void actionLeaveMultiMode();
    void actionJoinRoomZhubo();
    void testThreadFunc();
    void testTalkMode(); //通话接口 音量调节等等
    void testFreeVideoMode();
    void testHostMode();
private:
    std::string m_robotuser; //协作机的id
    std::condition_variable  m_cv;
    std::mutex m_mutex;
    std::thread _run_thread;
};

另外我在 runTests 函数中没有做什么事情,它只是把用例堆在一起,一起运行。
如果其中有用例发生阻塞或中断,什么也没管。

以上大概解释了为什么要使用测试框架的原因,如果你不用,你需要自己做更多事情,这些事情不简单而且很容易出错,我在自己写测试用例的时候,把那些通用公共的部分抽象出来,它就会变成一个“测试框架”的雏形。

总结:
一个测试框架的作用,就是帮助你完成那些通用的事情,测试断言,测试报告,测试用例管理,测试运行驱动等等。当然仅仅包含这些功能的框架,它还是比较原始的。
有影响力的测试框架往往包含了更多的内容,例如数据构造(mock),benchmark的用例支持,测试固件,以及产品层面的特性,例如易用性,移植性等等。C++目前最流行的单元测试框架有GoogleTest,CppUnit等。

相关文章

  • 如何当好一名测试经理

    一测试经理的能力框架测试经理最重要的能力是什么? 1、测试经理是做什么的 测试经理是做什么的?有的奔走于项目经理、...

  • 测试框架是干什么的?

    有一段时间,我负责一个语音通讯的SDK的集成自动化测试。 有别于单元测试的小粒度的测试,这种类型的测试一个SDK的...

  • Web 前端测试指南

    最近分享了关于前端测试的一些内容,关于开发如何通过测试来提升效率,常见的测试框架,以及什么的测试指标是我们关心的。...

  • Robotium原理

    测试框架图: Android测试环境的核心是Instrumentation框架,Instrumentation框架...

  • 小猿圈之测试用例的八大要素

    干测试也是很多人的青睐,那么测试具体是干什么的呢,有哪些操作流程,小猿圈老师给你们阐述一下吧,满足你们对测试的好奇...

  • 今天的练习,小学生画风

    桌上的葡萄干 不知道叫什么的花… 笔刷测试 明天竟然要上班,气

  • pytest接口自动化

    序言 接口测试脚本本质是被测试框架调用的,执行脚本时通过测试框架的命令来执行接口测试脚本=测试库+业务用例测试框架...

  • 接口测试思考——脚本编写(一)

    序言 接口测试脚本本质是被测试框架调用的,执行脚本时通过测试框架的命令来执行接口测试脚本=测试库+业务用例测试框架...

  • Pytest框架介绍、安装

    pytest是python测试框架,与python自带的unittest测试框架类似,但是比unittest框架使...

  • pytest知识点

    一、单元测试框架1.什么是单元测试框架单元测试框架是在自动化测试或者白盒测试中对软件的最小单元(函数、方法)进行测...

网友评论

      本文标题:测试框架是干什么的?

      本文链接:https://www.haomeiwen.com/subject/qrntfrtx.html