使用ddt实现unittest的参数化测试

0. 前言

本文介绍如何使用ddt库来完成unitest的参数化设置。

ddt的github地址

ddt的官方文档

1. 为什么需要参数化

我们在写单测中,需要考虑到各种场景,通过输入各种场景的值执行目的的方法,来判断输出是否是我们所期待的值。

如下代码代码所示,针对large_than_two方法进行了三种场景的校验写了三个单测,但其中逻辑代码是一致的,而只需要使用不同的参数值进行输入,导致有许多的重复代码进行复制粘贴。

from unittest.case import TestCase


def large_than_two(value) -> bool:
    return value > 2


class TestDemoCase(TestCase):

    def test_larger_than_two_with_three(self):
        self.assertTrue(large_than_two(3))

    def test_larger_than_two_with_eight(self):
        self.assertTrue(large_than_two(8))

    def test_larger_than_two_with_five(self):
        self.assertFalse(large_than_two(5))

2. 使用ddt实现参数化

首先需要通过pip来安装该库

pip install ddt

2.1 基本使用

我们在 TestCase 上添加 ddt 装饰器,然后在单测方法上添加 data 装饰器,并添加了 3 种场景的输入参数,如下代码所示:

from unittest.case import TestCase

from ddt import ddt, data


def large_than_two(value) -> bool:
    return value > 2


@ddt
class TestDemoCase(TestCase):

    @data(3, 8, 5)
    def test_larger_than_two(self, value):
        self.assertTrue(large_than_two(value))

执行上面的单测,输入出如下:

============================= test session starts =============================
collecting ... collected 3 items

test_demo.py::TestDemoCase::test_larger_than_two_with_three_1_3 PASSED   [ 33%]
test_demo.py::TestDemoCase::test_larger_than_two_with_three_2_8 PASSED   [ 66%]
test_demo.py::TestDemoCase::test_larger_than_two_with_three_3_5 PASSED   [100%]

============================== 3 passed in 0.02s ==============================

会发现,虽然我们写了一个单测用例,但是却执行了 3 个用例,这是因为 ddt 会将 data 的 3 个参数分别注入到 test_larger_than_two 方法中 value 中并执行单测。

在输出的单测信息中,会输出单测方法+第多少个单测+参数值来表示当前用例的执行。

通过这种方式可以减少我们的重复代码。

2.2 多个值使用参数化

当我们需要在一个单测用例中注入多个值时,可以在data中传入多个元组进行参数化,但执行单例时,会将元组注入到value中,我们将其解开则能拿到多个值。代码如下图所示:

def greater(v1, v2) -> bool:
    return v1 > v2


@ddt
class TestDemoCase(TestCase):

    @data(
        (3, 2),
        (4, 1),
        (8, 6))
    def test_greater(self, value):
        v1, v2 = value
        self.assertTrue(greater(v1, v2))

执行之后输出如下所示:

============================= test session starts =============================
collecting ... collected 3 items

test_demo.py::TestDemoCase::test_greater_1__3__2_ PASSED                 [ 33%]
test_demo.py::TestDemoCase::test_greater_2__4__1_ PASSED                 [ 66%]
test_demo.py::TestDemoCase::test_greater_3__8__6_ PASSED                 [100%]

============================== 3 passed in 0.02s ==============================

2.3 参数化扁平使用

元组中的数据可以由 ddt 解开后注入到单测方法中的参数中。只需要在单测方法前添加 unpack 装饰器即可。代码如下所示:

from unittest.case import TestCase

from ddt import ddt, data, unpack

@ddt
class TestDemoCase(TestCase):

    @unpack
    @data(
        (3, 2),
        (4, 1),
        (8, 6))
    def test_greater(self, v1, v2):
        self.assertTrue(greater(v1, v2))

执行后结果如下:

============================= test session starts =============================
collecting ... collected 3 items

test_demo.py::TestDemoCase::test_greater_1__3__2_ PASSED                 [ 33%]
test_demo.py::TestDemoCase::test_greater_2__4__1_ PASSED                 [ 66%]
test_demo.py::TestDemoCase::test_greater_3__8__6_ PASSED                 [100%]

============================== 3 passed in 0.02s ==============================

2.4 命名参数

我们还可以给传入的参数进行命名而不是元组的形式,传入的参数名称与单测方法中参数的变量名对应,则不需要对应顺序传入,可读性更强了。代码如下:

@ddt
class TestDemoCase(TestCase):

    @unpack
    @data(
        {"first": 3, "second": 2},
        {"first": 4, "second": 1},
        {"first": 8, "second": 6}
    )
    def test_greater(self, second, first):
        self.assertTrue(greater(first, second))

执行后输出:

============================= test session starts =============================
collecting ... collected 3 items

test_demo.py::TestDemoCase::test_greater_1 PASSED                        [ 33%]
test_demo.py::TestDemoCase::test_greater_2 PASSED                        [ 66%]
test_demo.py::TestDemoCase::test_greater_3 PASSED                        [100%]

============================== 3 passed in 0.01s ==============================

2.5 从json文件中读取参数进行单测

在某些业务中,输入的参数过于复杂,并且场景繁多,如果将参数数据全部放在单测代码中,则会显得繁重,而且代码不易读,ddt提供了从json文件中读取参数来作为单测的输入数据。

创建data.json文件,其中包含了参数数据。

[
  {
    "first": 3,
    "second": 2
  },
  {
    "first": 4,
    "second": 1
  },
  {
    "first": 8,
    "second": 6
  }
]

然后使用file_data作为单测方法的装饰器,并传入上面数据文件的路径。

from ddt import ddt, file_data

@ddt
class TestDemoCase(TestCase):

    @file_data("data.json")
    def test_greater(self, second, first):
        self.assertTrue(greater(first, second))

执行成功并输出:

============================= test session starts =============================
collecting ... collected 3 items

test_demo.py::TestDemoCase::test_greater_1 PASSED                        [ 33%]
test_demo.py::TestDemoCase::test_greater_2 PASSED                        [ 66%]
test_demo.py::TestDemoCase::test_greater_3 PASSED                        [100%]

============================== 3 passed in 0.02s ==============================

3. 总结

本文是介绍ddt的基本并常用的用法,如果想要深入使用可以参考官方文档。

其实ddt有个缺点是不能针对某一个单测方法进行单独的执行,必须要运行整个Unittest class才行,这样在调试的过程中非常不方便。

如果你看到本文其实我比较推荐你使用pytest来替代unittest使用,pytest中也有参数化的使用,并且可以单独的去运行每一个单测。

我是因为在做一个django项目,其中使用的是django test来写单测的,而django test是基于Unittest来实现的,所以只能使用ddt来实现参数化。

欢迎关注,互相学习,共同进步~

我的个人博客
公众号:编程黑洞

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值