给大家安利一款超好用的测试框架-pytest

pytest 是什么?

pytest是python中非常流行的ut测试框架,这个框架有多流行呢? 应该说现在比较新的开源项目基本都已经使用pytest用来写UT了,比如我们熟悉的requests,ansible,flask, pypy等库有兴趣的同学不妨去Github上看下对应的test目录下的测试文件,全部都是用pytest作为UT测试框架的。而且Pycharm,VSCode等IDE已经原生支持这个框架,只要在设置中将默认框架从unittest切换为pytest即可。并且这个框架在用户群体中也有非常高的评价,比如:

image.png

当然这个框架除了作为ut测试框架以外,用来做BDD或者E2E测试也是完全没有问题的。比如接口测试框架HttpRunner3.0就是直接包了一层pytest外加一些拓展实现的。

为什么pytest如此流行

笔者自己的经历来说,在接触pytest之前曾经在cucumber,behave,robotframework之间徘徊不定。直到发现了pytest这个框架以后,感觉就是duang~ 用起来各种流畅。下面来简单介绍框架提供的几个核心特点:

  1. 用例写作非常简练,没有模版代码,非常pythonic。写作一个简单的用例跟普通的代码相差无几,例如:

    def test_simple_case():
        b = [2, 3, 4]
        assert 1 in b
    

    运行测试只需要在同级目录下输入:

    $ pytest
    
  2. Assert重写,更加友好的错误信息,定位问题一目了然:

    pytest重载了默认的assert方法,你可以像书写普通的条件语句一样书写你的assert方法。再也不用记住其他框架里诸如AssertEqual, AssertContains这些关键字了。并且在遇到Assertion失败的时候,提供的上下文也非常简洁明了。比如l下面的assertion错误信息,可以非常直观的看到出错的地方在 index100的地方:

    
        def test_eq_list():
            long_list = [0, 1, 2] * 100
            a = long_list + [3, 4, 5]
            b = long_list + [4, 4, 5]
    >       assert a == b
    E       assert [0, 1, 2, 0, 1, 2, ...] == [0, 1, 2, 0, 1, 2, ...]
    E         At index 300 diff: 3 != 4
    E         Use -v to get the full diff
    
    
  3. Mark(类似其他框架里面的标签)功能:

    Mark用来标记测试用例,用来方便的为测试用例添加属性信息,以方便插件根据这些熟悉信息作一些特殊处理。比如内置的skip, skipif, xfail等mark,就分别用来表示跳过,条件式跳过,预期内的失败用例。比如下面的例子,可以标记某些case在windows的环境上跳过执行等:

    import sys
    
    import pytest
    
    
    def test_normal_case():
        """no skip mark function"""
        pass
    
    
    @pytest.mark.skip(reason="not implemented yet")
    def test_in_the_future():
        raise NotImplemented
    
    
    # class-level mark
    @pytest.mark.skipif(sys.platform == 'win32',
                        reason="not  applicable on windows x86")
    class TestPosixCalls(object):
    
        def test_only_available_in_linux(self):
            """will not be setup or run under 'win32' platform"""
    
    

    而一些其他插件比如pytest-repeat, pytest-timeout等,则分别使用@pytest.mark.repeat()@pytest.mark.timeout()来单独标记某些需要重复执行多次的用例以及用例执行执行超时控制(超时则停止执行并且标记为失败)。

  4. Fixture(类似其他框架里的setup/teardown,依赖注入,共享实例等)系统:

    1. 使用yield声明测试前与测试后执行代码,语法非常简洁,例如:

      @pytest.fixture()
      def db_session(scope="session"):
          db = DBSession('scratch')
          db.connect()
          yield db
          db.close()
      
      
      def test_query_data(db_session):
            # do something with db_session here
          assert pass
       
      def test_update_data(db_session):
            # do something with db_session here
          assert pass
      

      这里将db_session声明为一个fixture,并且作为参数传入测试用例,用例就可以直接基于这个db_session做操作。无需关心具体的数据库连接和断开时机。由于fixutre是支持function,class,module,session不同层级的共享,这里我们将scope设置为session,意思是这一轮测试里面,db.connect和db.close()方便按需进行实例化:

    2. 支持参数化用例声明,例如:

      import pytest
      
      
      @pytest.mark.parametrize("test_input,expected", [
          ("3+5", 8),
          ("2+4", 6),
          pytest.param("6*9", 42,
                       marks=pytest.mark.xfail),
      ])
      def test_eval(test_input, expected):
          assert eval(test_input) == expected
      
      

      在实际执行时会得到类似如下的输出,也就是同样的步骤在不同的输入情况下的输出用例:

      collected 3 items  
      fixture/test_parametrize.py::test_eval[3+5-8] PASSED                                                                                                              [ 33%]
      fixture/test_parametrize.py::test_eval[2+4-6] PASSED                                                                                                                 [ 66%]
      fixture/test_parametrize.py::test_eval[6*9-42] XFAIL                                                                                                                 [100%]
      
      
  1. 插件系统

    作为测试框架,pytest有着非常好的插件系统设计。框架本身基于pluggy框架开发,通过调用定义良好的hooks来实现配置,收集,执行和报告这些过程。官方收录的插件列表就有300+,并且插件开发也比较容易上手,只需要了解一下框架提供的几个hook和一些类的属性,有兴趣的同学也可以看下笔者的个人博客的介绍。

注:有些内容因为截图比较多,大家可以结合附件的PPT一起看。这里就不再赘述了。限于篇幅以及笔者水平,还有一些高级的功能无法给到家娓娓道来,有兴趣的同学可以直接去官网看教程。

总的来说,笔者的感慨就是好的框架想要走的长远都需要在三个点上花功夫:

  • 简单: 这个很大程度上决定你的受众有多少,也就是你能走多快的问题;

  • 可靠:我们当然不会选择一个简单易出错或者一拓展就懵逼的这种框架,可靠的框架会在使用中不断积累用户口碑,吸引更多用户加入。

  • 易拓展:这个基本上决定你能走多远,没有拓展性的框架一般寿命不会长久,很快就会被新的框架替代。pytest正是借助其良好的插件系统设计,拓展了许许多多非常实用的功能。毕竟,没人人比用户更懂自己的需求(乔布斯除外)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335