模拟测试mock,测试不得不会的技能

1,109 阅读6分钟

「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战」。

前言

在测试职业发展中,这项技能属于额外扩展的、但作为测试人员必知必会的测试技能之一,千万不要形成一种自己没有解决方案就依赖于开发解决问题的思想;先要想想自己为什么不能解决,要是自己该想想如何才能解决,如何落地解决方案等等;更要反思自己为什么不能解决?所以不管是mock技术还是其他测试技能,不仅得自己会,你还得教会别人,甚至影响整个团队。

Mock方式

什么是mock测试?

网络百度百科找到这样的释义,没错,它说的是mock测试: mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便于测试的测试方法。

不mock,会怎样?

个人认为在互联网高速发展的时代,并且是需要快速响应市场的时代,产品想要活、要获得更多的用户,那么它必须得快速更新迭代;

在互联网公司,一般常规半个月一迭代,快的更是几天一迭代,如果开发模式,是全栈开发工程师,那就是前后端一个人搞,不怕<死>的就来;

这样不晓得是好还是坏,总之时间是少不了的,测试也必不可少;那么问题来了,前后端开发分离,在需求评审后,如何分解任务?

  • 情况1:后端同学开始开发接口,前端拿到UI设计稿开始布局画面,但这时接口没出来?

excuse me?前端去围观后端同学?天天催促,出接口了没,等着联调?活该你加班的命!!!

难道领导不知道这中间的时间差就是损失吗,最后等后端开发完成才开始联调测试,这时留给测试同学的时间还有多少?周期紧凑,质量也堪忧!

  • 情况2:后端A开发接口必须依赖后端B同学开发的接口,那么他也在等吗?

你们是不是觉得迭代时间太漫长了,都想组队加班了?

  • 情况3:关于第三方服务,如果是性能场景,总不能去压人家的服务器吧,这时己方测试怎么办?

不需要特别关注第三方业务的时候,己方又该怎么办?

No,迭代周期远比你想的要短,QA/项目经理/技术经理/运营/客户都在追着项目进度,不要等着被追杀,这种滋味会影响项目的质量,也会让人心生厌恶。

怎么解决这种问题呢?

如此,这个mock的意义就出来了,前端不再依赖后端,后端A不再依赖后端B,而是并行开发,等达到一个所有任务都完成开发的时间点,

再集成测试<前后端联调测试>,至少比到了最后的环节才发现问题,尽量将提测的时间往前赶,虽然不是绝对的早;但是不失为一种不错的协同办公模式。

  • 怎样才能不依赖呢?

需求评审一过,确认需求,开发识别需要做的工作,前后端约定数据结构,给出接口文档,然后开始fire,我想没有比这更舒坦的工作方式了。

  • 说完了释义和问题及解决方案,接下来看实际应用

unittest测试框架

  • 作者本人习惯使用python,就以标准库unittest框架为例
需要有python基础;mock在python2中有独立的库,python3直接集成在unittest框架了。
  • 场景1:接口B依赖接口A,那么只有在接口A请求正常的情况下才能正常请求接口B
class Demo():
    def pay(self):
        """
        这是一个由B同学开发的支付接口,未完成;
        他会返回两种状态:成功or失败
        {"msg":"success","code":0,"reason":None}
        {"msg":"failed","code":-1,"reason":"支付帐号或密码错误"}
        reason:返回失败原因
        """
        pass
    
    
    def get_status(self):
        """根据支付结果状态"""
        res=self.pay()
        try:
            if res["msg"]=="success":
                return "支付成功"
            elif res["msg"]=="failed":
                return "支付失败"
            else:
                return "未知异常"
        except:
            return "Error,服务端异常"
  • 根据Demo模块设计单元测试用例
import unittest
from unittest import mock

class TestPayStatus(unittest.TestCase):
    """单元测试用例"""
    
    # 实例化对象
    demo=Demo()
    
    def test_success(self):
        """测试支付成功场景"""
        # 模拟支付成功响应
        self.demo.pay=mock.Mock(return_value={"msg":"success","code":0,"reason":None})
        status=self.demo.get_status()
        # 断言
        self.assertEqual(status, "支付成功")
        
    def test_failed(self):
        """测试支付失败场景"""
        # 模拟支付失败响应
        self.demo.pay=mock.Mock(return_value={"msg":"failed","code":-1,"reason":"支付帐号或密码错误"})
        status=self.demo.get_status()
        # 断言
        self.assertEqual(status, "支付失败")
    
    def test_error(self):
        """测试异常场景"""
        # 模拟支付失败响应
        self.demo.pay=mock.Mock(return_value={"msg":"error","code":-2,"reason":"未知异常"})
        status=self.demo.get_status()
        # 断言
        self.assertEqual(status, "未知异常")
    
    def test_error2(self):
        """测试服务端异常场景"""
        # 模拟支付失败响应
        self.demo.pay=mock.Mock(return_value={})
        status=self.demo.get_status()
        # 断言
        self.assertEqual(status, "Error,服务端异常")
  • 上面的案例,是不是很简单,并且用例覆盖率达到100%

flask框架

  • 除了unittest特有的mock之外,网络更是有诸多mock平台,当然还有一些语言特殊支持的mock工具包;
  • 这里再介绍一个flask框架,它是python开发的一个轻量级后台框架.

曾经在某金融网贷公司工作,需要对接多家小贷公司,每次上一个产品都需要联调接口,并且成功之后,其他接口的状态只能自己模拟,第三方极少再次配合;所以这需要己方解决这类难题!

实现mockserver

服务关于第三方联调,接口、请求参数是固定的,且url是配置在咱们数据库的,唯有请求参数中的状态字段不同,这样服务才能给出不同的响应结果;好在flask在构造api这一层十分方便。

  • 创建一个自己的api服务
#-*- coding:utf-8 -*-


from flask import Flask
from flask import jsonify
from flask import request, Response
import random
import time

app = Flask(__name__)

"""
    这里所有的接口我们才去返回json串
    所有的json传对应的value值都为随机的
"""

# 生成随机字符串
def random_str():
    # 待选随机数据
    data = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+=-"

    # 用时间来做随机播种
    random.seed(time.time())

    # 随机选取数据
    sa = []
    for i in range(8):
        sa.append(random.choice(data))
    salt = ''.join(sa)

    return salt

# 构建response
def make_response():
    content = '{"result": "%s", "data": "%s"}' % (random_str(), random_str())
    resp = Response(content)
    resp.headers["Access-Control-Origin"] = '*'

    return resp


# http GET
@app.route("/query", methods=["GET"])
def query():
    return jsonify(
        username=random_str(),
        password=random_str()
        )

# http POST
@app.route("/update", methods=["POST"])
def update():
    
    return make_response()
 

if __name__ == "__main__":

    app.run(debug=True)
# 响应结果
{
  "password": "3fJ1yGFL", 
  "username": "3fJ1yGFL"
}
  • 然后根据需求,将需要模拟的接口,根据入参判断之后,给出期望的结果,就能模拟各种状态在前端渲染显示出来了。

扩展

mock的方式还有很多,比如easy-mock,这在对于写代码而言,实现更加简单,只需要懂它特定的语法即可(即返回值);又如charles这款抓包工具,不要误以为它只有抓包功能,其实它还可以做接口请求的重定向,对于mock而言它就足够满足了。

1、easy-mock的github地址

2、Charles也能实现mock测试