前言
最近很多小伙伴问Python继承的问题以及继承的使用场景。我们常说面向对象的三个基本特性:封装、继承、多态。
封装这个其实没啥好说的,而多态在Python其实是不存在的,Python崇尚的是鸭子类型,一个对象只要看着像鸭子,走起来像鸭子,那它就是鸭子了。
而关于继承,我就不详细说继承的各种特性和特点了,网上一大堆,我聊聊他在自动化领域的常见用法。
简单的继承用法
假设我们现在正在编写测试用例脚本,写了一个测试类:
import pytest
import requests
class TestLogin:
def request(self):
"""业务代码"""
url = "https://www.baidu.com/s?ie=utf-8&wd=python"
response = requests.request(method="get", url=url)
def test_run(self):
self.request()
if __name__ == '__main__':
pytest.main(["-v", "-s"])
当上面这个测试类丢给测试引擎(pytest、unittest)去执行的时候,测试引擎会去执行test开头的测试方法,然后test_run()放就会去调用request()方法去发起请求。
那么现在有一个问题,如果我有多个测试脚本要写,那每个测试脚本都要写一遍上面的代码吗?这显然不科学,所以很多小伙伴就会用到继承。
我们稍微改一下代码,写一个父类,然后分别写一个请求百度的类,一个请求qq的类去继承这个父类,如下:
import pytest
import requests
class CaseBase:
def request(self):
pass
def test_run(self):
self.request()
class TestBaidu(CaseBase):
def request(self, *args, **kwargs):
url = "https://www.baidu.com/"
response = requests.request(method="get", url=url)
class TestQQ(CaseBase):
def request(self, *args, **kwargs):
url = "https://www.qq.com/"
response = requests.request(method="get", url=url)
if __name__ == '__main__':
pytest.main(["-v", "-s"])
如上代码,TestBaidu和TestQQ两个类都继承CaseBase,我们只需要重写父类的request()方法,把自己的请求过程写上去就好了。 然后将TestRegister和TestLogin丢给测试引擎,你会发现因为继承了父类CaseBase,我们不需要修改test_run()方法的内容,测试引擎依然会去执行并且调用request()也是正常的。
抽象基类
现在问题来了,如果你是跟别人一起协作写的代码,你已经写好父类了,虽然你的小伙伴继承了你的父类,但他非常头铁,本来只需要重写request方法就好了,他非得写一个reques_baidu()的方法。代码如下:
import pytest
import requests
class CaseBase:
def request(self):
pass
def test_run(self):
self.request()
class TestBaidu(CaseBase):
def request_baidu(self, *args, **kwargs):
url = "https://www.baidu.com/"
response = requests.request(method="get", url=url)
if __name__ == '__main__':
pytest.main(["-v", "-s"])
那么运行结果会怎样?
结果就是啥也不会发生,因为即没有重写父类的reques()方法,也没有重写test_run()方法,这个时候把测试类丢给测试引擎,测试引擎依然是去执行test_run()方法,然后就会去调用父类的request()方法,并且由于request()方法里面的代码是空的,因此啥也不会发生。
这个时候你的小伙伴跑过来找你,“跟你说你的框架太菜了吧,我写了请求方法,它都不去执行,真是渣渣!”(是不是有种想刀了他的冲动?)
那么,为了避免这种情况,我们可以使用Python的抽象基类,具体需要使用到Python内置的abc模块。
接下来,我们还是用回上面头铁小伙伴写的代码,但我们稍微对父类进行一些改动:
from abc import ABCMeta, abstractmethod
import pytest
import requests
class CaseBase(metaclass=ABCMeta):
@abstractmethod
def request(self):
pass
def test_run(self):
self.request()
class TestBaidu(CaseBase):
def request_baidu(self, *args, **kwargs):
url = "https://www.baidu.com/"
response = requests.request(method="get", url=url)
if __name__ == '__main__':
pytest.main(["-v", "-s"])
这个时候,你的小伙伴再运行代码,就会出现异常:
TypeError: Can't instantiate abstract class TestBaidu with abstract method request
翻译过来就是,TestBaidu这个子类没有重写父类request方法。
这就很完美的解决了小伙伴头铁的情况了,毕竟我规定好的事情,你得按照我的要求去做,不能头铁~
首先
class CaseBase(metaclass=ABCMeta)
这里的意思是将CaseBase以元类的方式进行创建并指定ABCMeta来限制创建类的方式。而
@abstractmethod
则是父类利用这个装饰器去限制子类必须要重写被装饰的方法,否则就抛出异常。
以上内容就是最常见的用法,但使用场景不设限,因为不只是编写测试脚本的时候,在很多场景都会用得上继承和抽象基类。
最后请记住:Python面向对象编程的语言。
欢迎来到testingpai.com!
注册 关于