社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

用 Hypothesis 快速测试你的 Python 代码

Python中文社区 • 4 年前 • 594 次点击  

介绍

无论你使用哪种编程语言或框架,测试都非常重要。Hypothesis是 Python 的一个高级测试库。它允许编写测试用例时参数化,然后生成使测试失败的简单易懂的测试数据。可以用更少的工作在代码中发现更多的bug。该测试库覆盖了大多数情况,并且确实可以帮助你查找代码中的错误。

这篇文章为展示了如何使用Hypothesis在Python中进行测试,并提供了一些示例。

我们如何区分测试?

在我们开始进行基于属性的测试之前,我们需要知道测试的一般区别。有不同的分组测试方法,两种最常见的方法基于测试方法和测试级别。让我们从大多数人已经听说的测试级别开始。本质上,存在四个测试级别(尽管人们可能也知道或定义其他级别):

  • 单元测试
  • 集成测试
  • 系统测试
  • 端到端测试

不同测试级别侧重专注于不同的事物。单元测试侧重于软件的特定部分或功能。这可以是单个功能或功能的一部分。相反,集成测试侧重于通过软件组件的接口进行协作。系统测试甚至更进一步,可以测试整个系统。

现在,我们将看看存在的各种各样的测试方法。

最常见和已知的是静态和动态测试。所谓静态测试(static testing)就是不实际运行被测软件,而只是静态地检查程序代码、界面或文档中可能存在的错误的过程。如果软件或其部分实际执行,我们称之为动态测试。编写单元测试和集成测试属于动态测试。

另一种常见的方法是盒式方法。基本上,它可以分为白盒测试和黑盒测试(以及灰盒测试作为两者的混合)。白盒测试可验证程序的内部结构或工作情况。黑盒测试与之相反,在黑盒测试中,应用程序被视为黑盒,并且对其交互进行测试。这意味着在不了解内部实现的情况下测试功能。

什么是基于属性的测试?

现在,我们快速了解了如何区分测试,您可能会问:什么是基于属性的测试?

基于属性的测试技术( Property-based testing),是指编写对你的代码来说为真的逻辑语句(即“属性”),然后使用自动化工具来生成测试输入(一般来说,是指某种特定类型的随机生成输入数据),并观察程序接受该输入时属性是否保持不变。如果某个输入违反了某一条属性,则用户证明程序存在一处错误,并找到一个能够演示该错误的便捷示例。

使用Hypothesis进行基于属性的测试

让我们举一个简单的例子。假设您有两个函数crement()decrement()。一个示例实现可能如下所示:

# increment_decrement.py

def increment(number: int) -> int:
    return number + 1


def decrement(number: int) -> int:
    return number - 1

您可能会为两者编写单元测试代码,如下所示:

# test_increment_decrement_pytest.py

from increment_decrement import decrement
from increment_decrement import increment

def test_increment():
    x = 5
    expected = 6
    actual = increment(x)
    assert actual == expected


def test_decrement():
    x = 5
    expected = 4
    actual = decrement(x)
    assert actual == expected

注意:测试代码是使用pytest框架编写的。

当然,您可以编写更多的测试脚本来测试具有不同值的两个函数,甚至可以对测试进行参数化。但是,最后您将使用预定义的值来测试这两个功能。

使用基于属性的测试库(例如Hypothesis )编写测试是不同的。在这里,您可以指定要测试的类型以及软件的工作方式或行为方式。然后该库根据指定的类型生成随机值来进行实际测试功能。

让我们看看如何使用Hypothesis来测试我们的两个功能。




    
# test_increment_decrement_hypothesis.py

from hypothesis import given
import hypothesis.strategies as st

from increment_decrement import decrement
from increment_decrement import increment


@given(st.integers())
def test_increment(x):
    expected = x + 1
    actual = increment(x)
    assert actual == expected


@given(st.integers())
def test_decrement(x):
    expected = x - 1
    actual = decrement(x)
    assert actual == expected

如您所见,这两个测试脚本都有一个参数xx的值是由Hypothesis使用integers()方法生成的。Hypothesis提供了各种方法。本质上,这些方法对应于内置类型或其他结构,并生成与给定类型匹配的随机数据。

听起来不错,不是吗?但是,如果我们想测试具有特定值的函数以确保它也可以使用该值怎么办?Hypothesis提供了一个@example()装饰器,您可以在其中定义一个值,即使该值不属于随机生成的测试数据集,也可以将该值传递给相应的函数。

让我们举个简单的例子:

# div.py

def div(dividend: int, divisor: int) -> int:
    return dividend // divisor

我们定义了一个函数div(),该函数接受一个除数和一个被除数并返回两者的商。请注意,这两个参数都是整型数据,因此结果也应该是整型数据,我们使用Python的//运算符执行整数除法。

为了测试div()函数,我们创建了一个新的测试文件test_div.py并编写了一个名为test_div()的测试脚本。

# test_div.py

from hypothesis import example
from hypothesis import given
import hypothesis.strategies as st

from div import div


@given(dividend=st.integers(), divisor=st.integers())
def test_div(dividend, divisor):
    if divisor == 0:
        expected = -1
    else:
        expected = dividend // divisor
    actual = div(dividend, divisor)
    assert actual == expected

同样,我们使用Hypothesisintegers()方法生成除数和被除数的值。我们编写的测试脚本可能通过也可能不会通过,具体取决于执行时Hypothesis产生的值。为了确保始终将值0传递给div()函数,我们将@example(1,0)添加到test_div()函数。因此,即使div() 不在随机生成的数据集中,也至少会用除数的值0调用一次。

如果我们按原样运行测试脚本,则test_div()将始终失败。因此,让我们修改div()函数来处理这种情况并使测试通过:

# div.py

def div(dividend: int, divisor: int) -> int:
    if divisor == 0:
        return -1
    return dividend // divisor

概要

本文主要讲了什么是基于属性的测试以及为什么有用。此外,您快速浏览了Hypothesis库,该库使您可以编写基于属性的测试并与pytest测试一起执行。


往期推荐



5分钟完全掌握PyPy


5 分钟掌握 Python 中常见的配置文件


OpenCV人工智能图像识别技术实操案例


点击下方阅读原文加入社区会员




点赞鼓励一下

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/99116
 
594 次点击