自动化用例开发过程中的常见技巧:代理模式

栏目: IT技术 · 发布时间: 4年前

内容简介:在上一次讲连接复用的时候,我实现了一个类用于接管这其实是一个很典型的具体完整解释请参考维基百科词条:https://en.wikipedia.org/wiki/Proxy_pattern
自动化用例开发过程中的常见技巧:代理模式

在上一次讲连接复用的时候,我实现了一个类用于接管 pymysql.Connection

class MySQLConnectionProxy:

    def __init__(self, *args, **kwargs):
        self._conn = pymysql.Connect(*args, **kwargs)

    def __getattr__(self, item):
        return getattr(self._conn, item)

这其实是一个很典型的 Proxy Pattern :给某一个对象提供一个代理,并由代理对象控制对原对象的引用

具体完整解释请参考维基百科词条:https://en.wikipedia.org/wiki/Proxy_pattern

代理模式应该是一种比较容易理解的设计模式,你可以把它类比成服务部署中的 nginxapache http 这类服务,它不暴露原始的请求资源地址(对象),而是让nginx(proxy)来接管client(调用方)的所有请求,具备了通过nginx(proxy)植入一些额外的能力来实现对原始资源的扩展、控制等。

自动化用例开发过程中的常见技巧:代理模式

这种模式的调用时序可以看下图:

自动化用例开发过程中的常见技巧:代理模式

我个人认为代理模式存在几点优势:

  • 代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。

  • 代理模式可以控制对真实对象的使用权限

  • 代理接管了对真实对象的调用,实现AOP(切面编程)的能力

实现一个Proxy类

Python 中因为有着 __getattr____setattr__ 这样的magic method,所以实现一个通用的Proxy类是非常方便的事情:

class Proxy:
    def __init__(self, subject):
        self._subject = subject
    
    def __getattr__(self, item):
        return getattr(self._subject, item)
    
    def __setattr__(self, item, value):
        if item == "_subject":
            super().__setattr__(item, value)
        else:
            setattr(self._subject, item, value)

    
class Student:
    def __init__(self, name, age: int):
        self.name = name
        self.age = age
    
    def info(self):
        return "name: {}\tage:{}".format(self.name, self.age)
mike = Proxy(Student("mike", 20))

print(mike.info())
mike.age = 24
print(mike.info())
print(type(mike))

name: mike	age:20
name: mike	age:24
<class '__main__.Proxy'>

可以看到上面十行代码就实现了通用的Proxy,不过这样的Proxy看上去并没有太多实际作用,于是我稍微扩展下,让其能够实现 beforeafter 这样的事件钩子:

class Proxy:
    def __init__(self, subject):
        self._subject = subject
        self._handlers = dict()
    
    def __getattr__(self, item):
        before = "before_{}".format(item)
        if before in self._handlers:
            self._handlers[before](item)
        
        ret = getattr(self._subject, item)
        
        after = "after_{}".format(item)
        if after in self._handlers:
            self._handlers[after](item)
        return ret
    
    def register(self, handler, method: str, scope: str):
        """注册任意函数的拦截器,实现对任意函数的after、before的钩子"""
        self._handlers["{}_{}".format(scope, method)] = handler
        
    
    def __setattr__(self, item, value):
        if item == "_subject" or item == "_handlers":
            super().__setattr__(item, value)
        else:
            setattr(self._subject, item, value)
            
  def print_before(item):
    print("before invoke {} method".format(item))

def print_after(item):
    print("after invoke {} method".format(item))


jack = Proxy(Student("jack", 16))
jack.register(print_before, "info", "before")
jack.register(print_after, "info", "after")
jack.info()
>>> before invoke info method
>>> after invoke info method
>>> 'name: jack\tage:16'

以上代码可能会对不太熟悉Python编程的自动化测试人员产生很大的困扰,不用太紧张,如果在你的项目要应用到代理模式,你可以针对要具体代理的对象进行具体的实现,不需要用到这么多magic method造成理解的障碍。

自动化测试用的应用

在上一次讲连接复用的末尾,我抛出了几个问题:

  • 如果 mysql 连接被服务端主动关闭了怎么办?

  • 因为是单例模式,如何防止有用例主动关闭mysql连接而影响其他用例?

然后我再抛出一个需求: 如何让框架自动记录mysql的查询记录,而不是手动去打日志?

以上三个问题其实在代理模式下都可以非常方便、优雅的来解决掉:

import pymysql


class CursorProxy(Proxy):
    
    def execute(*args, **kwargs):
        # 这里可以实现日志记录
        return getattr(self._subject, "execute")(*args, **kwargs)
        


class MySQLConnectionProxy:

    def __init__(self, *args, **kwargs):
        self._conn = pymysql.Connect(*args, **kwargs)

    def __getattr__(self, item):
        if self._conn.open:  # 这里可以检查是否被动关闭,然后实现重连
            self._conn.connect()
        
        if item == "close":  # 当用例要主动关闭时,无视该调用即可
            return
        
        if item == "cursor":  # 游标对象也需要代理
            def _curor(*args, **kwargs):
                return CurorProxy(getattr(self._subject, item)(*args, **kwargs))
            return _curor
        
        return getattr(self._conn, item)

结合这两节的内容来看,通过不太多的代码行数,在目前的自动化测试框架中已经实现了中间件client以下能力:

  • 连接的复用

  • 连接的自动重试、关闭保护

  • client请求内容的自动化日志记录

最后我抛出一个注意事项:在Python下使用代理模式后,能不能保留下原对象的上下文管理器(Context Manager)的特性?感兴趣的童鞋可以留言告知

自动化用例开发过程中的常见技巧:代理模式

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

C++程序设计原理与实践

C++程序设计原理与实践

(美)Bjarne Stroustrup / 王刚 等 / 机械工业出版社 / 2010.7 / 108.00元

本书是经典程序设计思想与C++开发实践的完美结合,是C++之父回归校园后对C++编程原理和技巧的全新阐述。书中全面地介绍了程序设计基本原理,包括基本概念、设计和编程技术、语言特性以及标准库等,教你学会如何编写具有输入、输出、计算以及简单图形显示等功能的程序。此外,本书通过对C++思想和历史的讨论、对经典实例(如矩阵运算、文本处理、测试以及嵌入式系统程序设计)的展示,以及对C语言的简单描述,为你呈现......一起来看看 《C++程序设计原理与实践》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具