普通类的不同

unittest 核心是通过文件夹等模式把一个个测试对象给添加到测试套件里面,然后进行驱动。
unittest 框架性提供了一些便利,会导致平时写代码根据函数和函数调用顺序,触发的对象在其他测试对象 (成员函数内) 是不可识别的。
context 是上下文,这里先不考虑使用 db 例如 Redis 做缓存和全局对象这种方式,先看看普通类的实现。

# 加了类名Test,除非用命令行执行,ide里面会判断是一个测试类对象
class Demo():
   def test_login(self):
      self.user = "maomao"

   def test_start_task(self):
      print(f"{self.user}开始任务")

if __name__ == '__main__':
   t = Demo()
   t.test_login()
   t.test_start_task()

可见,只要先调用了 test_login(),等于激活了 self.user,下面的 test_start_task 里面的 self.user 就等于 maomao。

unittest 上下文

把上面的 Demo 改成 TestDemo,支持 unittest 来测试下。

import unittest

class Demo(unittest.TestCase):
    def test_login(self):
        self.user = "maomao"
    def test_start_task(self):
       print(f"{self.user}开始任务")

if __name__ == '__main__':
   unittest.main()

这个会输出 AttributeError: 'Demo' object has no attribute 'user'
熟悉 unittest 的都知道他有每个模块,每个类,每个方法 (最小测试对象) 的前后置函数,每个方法之间的对象是隔离的。
只要是语言在计算机原理没有变化的情况下,白话来说,往上访问对于当前来说都是共享内存的区域,在方法层面往上访问就是类,共享区域就是类变量,类变量是晚于 classmethod 需要注意。
类层面往上访问就是模块,模块共享区域就是模块的init.py。
unittest 的函数对象访问顺序是遵循 ascii 码规则,函数对象名称前面 test_a_xxx,test_b_xxx...依次往下就行。

使用 unittest.main() 方式去驱动。

如果想让 test_start_task 可以识别到 user,就需要把 user 添加到类变量区域。

class Demo(unittest.TestCase):
    temp = []
    def test_a_login(self):
       self.user = "maomao"
       self.temp.append(self.user)
    def test_b_start_task(self):
      # 不推荐使用print(f"{self.temp[0]}开始任务1")
       self.user = self.temp.pop()
       print(f"{self.user}开始任务1")
       print(self.temp) #长度为0

不推荐的原因是 [0] 毕竟是魔术,可读性低,是不是觉得 pop() 返回并且清除列表内部的,更像一锤子买卖。
pop 的源码描述,remove and return item at index (default last) 默认是删除最后一个并且返回

# 上面代码下添加这个
def test_c_replay_start_task(self):
      print(f"{self.temp[0]}开始任务2")

结果会发现依然可以识别,由此可见 temp 在经过 test_login() 被添加后,temp 里面就有了 user。

下标 [0] 是不符合可读性的,那么 Python 的字典具备可读性,查询是 O(1) 的行为,是最合适的,这里完成修改下代码。

以后包含上下文的都可以用以下代码使用。

推荐解决方案

class Demo(unittest.TestCase):
   # 如果上下文需要用到的
   temp = {}

   def test_a_login(self):
      self.user = "maomao"
      self.temp["user"] = self.user

   def test_b_start_task(self):
      print(f'{self.temp["user"]}开始任务1')

   def test_c_replay_start_task(self):
      print(f'{self.temp["user"]}开始任务2')

if __name__ == '__main__':
   unittest.main()

PS:unittest 也是支持命令行的,-m 代表 import 导入,后面跟着 unittest 就是导入 ut。
py -3 -m unittest xxx.py


↙↙↙阅读原文可查看相关链接,并与作者交流