接口测试(十)—— telnet和python代码测试dubbo接口

目录

一、传智健康项目介绍

1、项目描述

2、目标用户群体

3、项目模块

4、系统框架

二、Dubbo接口测试

1、RPC

2、Dubbo

3、查阅API文档

三、Telnet工具远程调用

1、启用telnet

2、telnet远程连接服务

3、telnet调用服务接口

四、python借助dubbo远程调用

1、安装dubboclient

2、实现步骤

3、会员服务(入门)

4、其他模块

5、分析bug来源

6、现有问题

五、接口自动化测试框架封装Dubbo接口

1、核心模块

2、基础服务对象封装

3、服务对象封装

4、测试用例对象封装

5、参数化

6、接口自动化框架封装

7、测试报告


一、传智健康项目介绍

1、项目描述

传智健康管理系统,是一款应用于健康管理机构的业务系统。采用可视化界面管理,提高健康管理师工作效率,加强与患者间的互动。

项目地址:传智健康

2、目标用户群体

3、项目模块

会员服务、预约服务、体检报告服务、健康评估服务、健康干预服务 

4、系统框架

前端:http://mobile-health-test.itheima.net
后端:http://manager-health-test.itheima.net
 

 

二、Dubbo接口测试

1、RPC

  • 远程过程调用(Remote Procedure Call):像调用本地方法一样,调用远程方法。
  • 常见的RPC框架有 Dubbo、Thrift、grpc 

2、Dubbo

  • Dubbo是一款高性能、轻量级、基于Java的开源RPC框架(最早由阿里开源,2018年贡献给了Apache组织)
  • Dubbo接口的作用:远程调用 java 写的方法。 需要传参、获取返回值。

3、查阅API文档

从中获取哪些信息?

  • 服务名
  • 方法名
  • 参数类型、返回值类型

java中 方法定义语法结构
返回值类型 方法名(数据类型 形参1,数据类型 形参2,....)
void:代表没有返回值、没有参数。

三、Telnet工具远程调用

1、启用telnet

2、telnet远程连接服务

连接语法:telnet IP 端口号

 

3、telnet调用服务接口

命令格式:invoke 服务名.方法名(实参)
示例:invoke MemberService.findByTelephone("13020210001")

四、python借助dubbo远程调用

Dubboclient,封装了 telnetlib 库。 telnetlib 是 python 内置模块,可实现远程调用 Dubbo 接口 

1、安装dubboclient

pip install dubboclient

查验:

  • 在 pip 中:pip list 或 pip show dubboclient
  • 在 pycharm中:file - settings - 项目名下的 python 解释器列表

2、实现步骤

1. 导包 from dubboclient import DubboClient
2. 创建 DubboClient类实例,指定 IP 和 port
3. 使用 实例调用 invoke() 方法。 传入 :服务名、方法名、实参(方法使用)。获取响应结果
4. 打印响应结果

3、会员服务(入门)

3.1 案例1

根据手机号,查询会员信息(传递 普通参数)

dubbo> ls -l MemberService
        com.itheima.pojo.Member findByTelephone(java.lang.String)

接口定义:Member findByTelephone(String telephone)

参数:
    字符串格式手机号。唯一
返回值:
    成功:返回 会员的信息内容。string类型 包裹的 字典数据。
    失败:返回 null。string类型

 实现代码:

# 1. 导包 from dubboclient import DubboClient
from dubboclient import DubboClient

# 2. 创建 DubboClient类实例,指定 IP 和 port
dubboclt = DubboClient("211.103.136.244", 6502)

# 3. 使用 实例调用 invoke() 方法。 传入 :服务名、方法名、实参(方法使用)。获取响应结果
resp = dubboclt.invoke("MemberService", "findByTelephone", "13020210001")

# 4. 打印响应结果
print("响应结果 =", resp)
print("type(resp) =", type(resp))

3.2 案例2

添加会员(传递 对象参数)

dubbo> ls -l MemberService
        void add(com.itheima.pojo.Member)

接口定义:void add(Member member)

参数:
    1. 自定义类 做 参数,根据接口文档,组织 “字典” 格式数据传参
    2. 给字典增加 键k:”class“ ,值v:指明 类 对应的 完整 包名和类名
        如:"class”:"com.itheima.pojo.Member"
        ls -l MemberService 可以查看完整包名和类名。
        区分自定义类: 包名不以“java.”开头。一般采用:com.公司名.项目名.类名
返回值:
    成功:返回 null
    失败:返回 Failed

 实现代码:

# 1. 导包
from dubboclient import DubboClient

# 2. 创建 dubboclient 实例
dubboclt = DubboClient("211.103.136.244", 6502)
# 准备 add 方法,所需要的数据
info = {"id": 911, "name": "杜甫", "phoneNumber": "13048379884"}
# 如果 class 已经存在,覆盖原有class值; 如果不存在 class,新增一组 元素到 字典中。
info["class"] = "com.itheima.pojo.Member"

# 3. 调用 invoke 传 服务名、方法名、实参。得响应结果
resp = dubboclt.invoke("MemberService", "add", info)

# 4. 打印
print("响应结果 =", resp)
print("type(resp) =", type(resp))

3.3 案例3

根据日期统计会员数(传递 字符串列表)

dubbo> ls -l MemberService
        java.util.List findMemberCountByMonths(java.util.List)

接口定义:List<Integer> findMemberCountByMonths(List<String> months)

参数:
    1. 字符串列表。用字符串表示年、月,用“.”衔接
    如:["2021.3", "2021.9"]
返回值:
    成功:返回列表,对应参数设置的月份的会员数。
    失败:Failed

 实现代码:

# 1. 导包
from dubboclient import DubboClient

# 2. 创建 dubboclient 实例
dubboclt = DubboClient("211.103.136.244", 6502)

# 3. 用实例 调用invoke() ,传入 服务名、方法名、实参。 得响应结果
months = ["2021-7"]
resp = dubboclt.invoke("MemberService", "findMemberCountByMonths", months)

# 4. 查看响应结果
print("响应结果 =", resp)
print("type(resp) =", type(resp))

4、其他模块

4.1 添加预约设置

dubbo> ls -l OrderSettingService
        void add(java.util.List)

接口定义:void add(List<OrderSetting> list)

参数:
    1. 字典列表。字典有 orderDate 和 number 两个字段。
        如:[{"orderDate":"2021-09-20 16:45:12","number":20}]
    2. 日期格式:"2021-09-20 16:45:12",必须包含时分秒,否则失败。

返回值:
    成功:null
    失败:Failed

 实现代码:

# 1. 导包
from dubboclient import DubboClient

# 2. 创建 dubboclient 实例
dubboclt = DubboClient("211.103.136.244", 6502)
# 准备 add 方法,所需要的数据
info = [{"orderDate": "2021-05-18 18:89:02", "number": 346}]

# 3. 调用 invoke 传 服务名、方法名、实参。得响应结果
resp = dubboclt.invoke("OrderSettingService", "add", info)

# 4. 打印
print("响应结果 =", resp)
print("type(resp) =", type(resp))

4.2 按月统计预约设置信息

dubbo> ls -l OrderSettingService
        java.util.List getOrderSettingByMonth(java.lang.String)

接口定义:List getOrderSettingByMonth(String date)

参数:
    字符串,如:"2021-09"

返回值:
    成功:返回字符串类型数据,字符串内容为列表
    失败:Failed

 实现代码:

# 1. 导包
from dubboclient import DubboClient

# 2. 创建 dubboclient 实例
dubboclt = DubboClient("211.103.136.244", 6502)
# 月份
moths = "2021.02"

# 3. 调用 invoke 传 服务名、方法名、实参。得响应结果
resp = dubboclt.invoke("OrderSettingService", "getOrderSettingByMonth", moths)

# 4. 打印
print("响应结果 =", resp)
print("type(resp) =", type(resp))

4.3 根据日期修改预约设置数量

dubbo> ls -l OrderSettingService
        void editNumberByDate(com.itheima.pojo.OrderSetting)

接口定义:void editNumberByDate(OrderSetting orderSetting)

参数:
    1. 自定义类,用 字典 根据接口文档组织数据
    2. 需要使用 class 指定参数对象的类型
    如:{"orderDate":"2021-10-13 21:04:33","number":15,
"class":"com.itheima.pojo.OrderSetting"}
    3. 日期格式为:"2021-10-13 21:04:33",必须包含时分秒

返回值:
    成功:null
    失败:Failed

实现代码:

# 1. 导包
from dubboclient import DubboClient

# 2. 创建 dubboclient 实例
dubboclt = DubboClient("211.103.136.244", 6502)
# 日期 和 设置数据
date = {"orderDate": "2021-06-15 16:99:77", "number": 120}
date["class"] = "com.itheima.pojo.OrderSetting"

# 3. 调用 invoke 传 服务名、方法名、实参。得响应结果
resp = dubboclt.invoke("OrderSettingService", "editNumberByDate", date)

# 4. 打印
print("响应结果 =", resp)
print("type(resp) =", type(resp))

4.4 根据用户名查询用户信息

dubbo> ls -l UserService
        com.itheima.pojo.User findByUsername(java.lang.String)

接口定义:User findByUsername(String username)

参数:字符串类型,如:'admin'

返回值:
    用户存在:返回用户信息
    用户不存在:返回 null

 实现代码:

# 1. 导包
from dubboclient import DubboClient

# 2. 创建 dubboclient 实例
dubboclt = DubboClient("211.103.136.244", 6502)
# 管理用户名
name = "admin"

# 3. 调用 invoke 传 服务名、方法名、实参。得响应结果
resp = dubboclt.invoke("UserService", "findByUsername", name)

# 4. 打印
print("响应结果 =", resp)
print("type(resp) =", type(resp))

5、分析bug来源

抓取接口数据,分析bug是前端还是后端。

6、现有问题

远程调用的 7个dubbo接口 存在的问题:
1. 代码有 大量冗余
2. 测试接口时,除了要给 测试数据之外, 还需要 指定 服务名、方法名
3. 传参时,除了要考虑测试数据外,还要分析是否要添加 class 字段 及 对应数据。
4. 返回的数据类型统一为 string(不具体)


封装目标
1. 只关心:测试数据、响应结果
2. 返回的结果 分别为 不同的 具体类型。

五、接口自动化测试框架封装Dubbo接口

1、核心模块

2、基础服务对象封装

from dubboclient import DubboClient

class BaseService(object):

    def __init__(self):
        self.dubbo_client = DubboClient("211.103.136.244", 6502)

3、服务对象封装

3.1 会员服务封装

"""
类名:MemberService,继承于 BaseService
实例属性:
    服务名称:service_name,赋值为 'MemberService'
实例方法:
    def __init__(self):
        # 先调父类__init__(),再添加实例属性 service_name

    def find_by_telephone(self, telephone):
        # 功能:根据手机号查询会员信息
        # :param telephone: 手机号
        # :return: 1. 会员存在,返回会员信息 2. 会员不存在,返回None

    def find_member_count_by_months(self, data_list):
        # 功能:根据日期统计会员数
        # :param date_list: 日期列表,格式如:["2021.7"]
        # :return: 返回列表,列表元素为对应月份的会员数,如:[10]

    def add(self, info): 添加会员
        # 功能:添加会员
        # :param info: 会员信息的字典格式数据,参考接口文档填入字段数据,手机号需要唯一
        # 如:{"fileNumber":"D0001", "name":"李白", "phoneNumber":"13020210002"}
        # :return: 添加成功返回True, 添加失败返回False

验证结果:
    # 1. 实例化对象
    # 2. 通过实例对象调用实例方法
    # 2.1 根据手机号查询会员信息
    # 2.2 根据日期统计会员数
    # 2.3 添加会员
"""
import json
from day02.base_service import BaseService

# 将 会员服务 封装成 会员服务类
class MemberService(BaseService):
    def __init__(self):
        super().__init__() # 调用父类 init 方法
        self.service_name = "MemberService"

    def find_by_telephone(self, tel):
        resp = self.dubbo_client.invoke(self.service_name, "findByTelephone", tel)
        if resp == "null":
            return None
        else:
            # 作用:将 string类型的 数据,还原回成 字典 或 列表 数据。
            return json.loads(resp)

    def find_member_count_by_months(self, months):
        resp = self.dubbo_client.invoke(self.service_name, "findMemberCountByMonths", months)
        # 作用:将 string类型的 数据,还原回成 字典 或 列表 数据。
        return json.loads(resp)

    def add(self, info):
        """
        :param info: 代表 用户 传入的 测试数据,没有 class 元素
        :return:
        """
        # 如果 class 已经存在,覆盖原有class值; 如果不存在 class,新增一组 元素到 字典中。
        info["class"] = "com.itheima.pojo.Member"
        # 3. 调用 invoke 传 服务名、方法名、实参。得响应结果
        resp = self.dubbo_client.invoke(self.service_name, "add", info)
        if resp == "null":
            return True
        else:
            return False

if __name__ == '__main__':
    ms = MemberService()
    resp = ms.find_by_telephone("13020210001")
    print("响应结果 =", resp)
    print("type(resp) =", type(resp))

    print("=" * 66)

    months = ["2021-6"]
    ms = MemberService()
    resp = ms.find_member_count_by_months(months)
    print("响应结果 =", resp)
    print("type(resp) =", type(resp))

    print("&" * 66)

    # 准备 add 方法,所需要的数据
    info = {"id": 911, "name": "杜甫", "phoneNumber": "13048379041"}
    ms = MemberService()
    resp = ms.add(info)
    print("响应结果 =", resp)
    print("type(resp) =", type(resp))

3.2 预约设置服务封装

"""
类名:OrderSettingService,继承于 BaseService
实例属性:
    服务名称:service_name,赋值为 'OrderSettingService'
实例方法:
    def __init__(self):
        # 先调父类__init__(),再添加实例属性 service_name

    def add(self, date_list):
        # 功能:添加预约设置
        # :param date_list:
            # 1. 日期列表,如:[{"orderDate":"2021-09-20 16:45:12","number":20}]
            # 2. 日期格式为:"2021-09-20 16:45:12",必须包括时分秒
        # :return: 设置成功返回True, 设置失败返回False

    def get_order_setting_by_month(self, date):
        # 功能:按月统计预约设置信息
        # :param date: 日期,如:"2021-08"
        # :return: 列表,指定月份的预约信息

    def edit_number_by_date(self, info): 根据日期修改预约设置数量
        # 功能:根据日期修改预约设置数量
        # :param info:
            # 1. 预约设置的字典格式数据,参考接口文档填入字段数据
            # 2. 如:{"orderDate":"2021-09-19 17:45:12","number":15}
            # 3. 日期格式为:"2021-09-19 17:45:12",必须包括时分秒
            # 4. 添加 "class":"com.itheima.pojo.OrderSetting"
        # :return: 修改成功返回 True, 修改失败返回 False
验证结果:
    # 1. 实例化对象
    # 2. 通过实例对象调用实例方法
    # 2.1 添加预约设置
    # 2.2 按月统计预约设置信息
# 2.3 根据日期修改预约设置数量
"""
import json
from day02.base_service import BaseService

# 封装 预约设置服务类
class OrderSettingService(BaseService):
    def __init__(self):
        super().__init__()
        self.service_name = "OrderSettingService"

    def add(self, date_list):
        # 功能:添加预约设置
        # :param date_list:
            # 1. 日期列表,如:[{"orderDate":"2021-09-20 16:45:12","number":20}]
            # 2. 日期格式为:"2021-09-20 16:45:12",必须包括时分秒
        # :return: 设置成功返回 True, 设置失败返回 False
        resp = self.dubbo_client.invoke(self.service_name, "add", date_list)
        if resp == "Failed":
            return False
        else:
            return True

    def get_order_setting_by_month(self, month):
        # 功能:按月统计预约设置信息
        # :param months: 日期,如:"2021-08"
        # :return: 列表,指定月份的预约信息
        resp = self.dubbo_client.invoke(self.service_name, "getOrderSettingByMonth", month)
        if resp == "Failed":
            return None
        else:
            return json.loads(resp)

    def edit_number_by_date(self, date):
        # 功能:根据日期修改预约设置数量
        # :param info:
            # 1. 预约设置的字典格式数据,参考接口文档填入字段数据
            # 2. 如:{"orderDate":"2021-09-19 17:45:12","number":15}
            # 3. 日期格式为:"2021-09-19 17:45:12",必须包括时分秒
            # 4. 添加 "class":"com.itheima.pojo.OrderSetting"
            # :return: 修改成功返回 True, 修改失败返回 False
        date["class"] = "com.itheima.pojo.OrderSetting"
        # 3. 调用 invoke 传 服务名、方法名、实参。得响应结果
        resp = self.dubbo_client.invoke(self.service_name, "editNumberByDate", date)
        if resp == "Failed":
            return False
        else:
            return True

if __name__ == '__main__':
    oss = OrderSettingService()

    # 准备 add 方法,所需要的数据
    info = [{"orderDate": "2021-05-18", "number": 346}]
    resp = oss.add(info)
    print("响应结果 =", resp)
    print("type(resp) =", type(resp))

    print("============== 按月统计预约设置信息 ===========")
    oss = OrderSettingService()
    # 月份
    months = "2021.02"
    resp = oss.get_order_setting_by_month(months)
    print("响应结果 =", resp)
    print("type(resp) =", type(resp))

    print("============== 根据日期修改预约设置数量 ===========")
    # 日期 和 设置数据
    date = {"orderDate": "2021-06-15 16:99:77", "number": 120}
    oss = OrderSettingService()
    resp = oss.edit_number_by_date(date)
    print("响应结果 =", resp)
    print("type(resp) =", type(resp))

3.3 用户服务封装

"""
类名:UserService,继承于BaseService
实例属性:
    服务名称:service_name,赋值为'UserService'
实例方法:
    def __init__(self):
        # 先调父类__init__(),再添加实例属性 service_name

    def find_by_username(self, username):
        # 功能:根据用户名查询用户信息
        # :param username: 用户名
        # :return: 1. 如果用户存在,返回用户信息 2. 如果不存在,返回 None
验证结果:
    # 1. 实例化对象
    # 2. 通过实例对象调用实例方法
"""
import json
from day02.base_service import BaseService

# 封装 用户服务类
class UserService(BaseService):
    def __init__(self):
        super().__init__()

    def find_by_user_name(self, name):
        # 3. 调用 invoke 传 服务名、方法名、实参。得响应结果
        resp = self.dubbo_client.invoke("UserService", "findByUsername", name)
        if resp == "null":
            return None
        else:
            return json.loads(resp)

if __name__ == '__main__':
    # 管理员用户名
    name = "李白"
    us = UserService()
    resp = us.find_by_user_name(name)
    print("响应结果 =", resp)
    print("type(resp) =", type(resp))

4、测试用例对象封装

import unittest
# 借助 unittest 框架,封装测试类,从 TestCase 继承
from day02.py02_会员服务类封装设计 import MemberService

class TestFindByTelephone(unittest.TestCase):
    ms = None
    @classmethod
    def setUpClass(cls) -> None:
        # 创建MemberService实例
        cls.ms = MemberService()

    def test01_tel_exists(self):
        tel = "13020210001"
        resp = self.ms.find_by_telephone(tel)
        print("手机号存在 =", resp)
        self.assertEqual("13020210001", resp.get("phoneNumber"))

    def test02_tel_not_exists(self):
        tel = "13020218973"
        resp = self.ms.find_by_telephone(tel)
        print("手机号不存在 =", resp)
        self.assertEqual(None, resp)

    def test03_tel_has_special_char(self):
        tel = "1302021abc#"
        resp = self.ms.find_by_telephone(tel)
        print("手机号含有字母特殊字符 =", resp)
        self.assertEqual(None, resp)

5、参数化

1. 导包 from parameterized import parameterized
2. 在 通用测试方法上一行,@parameterized.expand()
3. 给 expand() 传入 [(),(),()] 类型的数据。
4. 修改 通用测试方法,添加形参,个数、顺序与 () 数据一致。
5. 在 通用测试方法 使用形参

import unittest
from day02.py02_会员服务类封装设计 import MemberService
from parameterized import parameterized
# 借助 unittest 框架,封装测试类,从 TestCase 继承
class TestMemberService(unittest.TestCase):
    ms = None
    @classmethod
    def setUpClass(cls) -> None:
        cls.ms = MemberService() # 创建MemberService实例

    # 通用测试方法(参数化)
    @parameterized.expand([("13020210001", "13020210001"),
                            ("13020218973", None),
                            ("1302021abc#", None)])
    def test_findByTelephone(self, tel, except_data):
        # print("tel =", tel, "except_data =", except_data)
        resp = self.ms.find_by_telephone(tel)
        if resp is None:
            self.assertEqual(None, resp)
        else:
            self.assertEqual(except_data, resp.get("phoneNumber"))

    @parameterized.expand([(["2021.5"], [3]),
                            (["2017.4"], [0])])
    def test_findMemberCountByMonths(self, month, except_data):
        resp = self.ms.find_member_count_by_months(month)
        print("============ resp =============", resp)
        self.assertEqual(except_data, resp)

6、接口自动化框架封装

7、测试报告

# 导包
import unittest
from htmltestreport import HTMLTestReport

# 创建 suite 实例
from scripts.test_member_service import TestMemberService
suite = unittest.TestSuite()

# 添加测试用例
suite.addTest(unittest.makeSuite(TestMemberService))

# 创建 HTMLTestReport 类对象
runner = HTMLTestReport("./report/传智健康测试报告.html", description="描述", title="标题")

# 调用 run() 传入 suite
runner.run(suite)
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

心上学事上练

感谢您的支持,让我们一起成长

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

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

打赏作者

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

抵扣说明:

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

余额充值