当前位置: 首页 > 科技观察

使用PyHamcrest执行稳健的单元测试

时间:2023-03-15 21:15:17 科技观察

使用此框架编写断言并提高开发测试的准确性。测试金字塔的底部是单元测试。单元测试一次测试一个代码单元,通常是一个函数或方法。通常,单独的单元测试旨在通过??功能或特定分支测试特定的执行流程,这使得将失败的单元测试映射到导致它们失败的错误变得容易。理想情况下,单元测试使用很少或不使用外部资源,隔离它们并使它们更快。单元测试套件通过在开发过程的早期发现问题来帮助维护高质量的产品。有效的单元测试可以在代码离开开发人员的机器之前捕获错误,或者至少在特定分支上的持续集成环境中。这标志着好的和坏的单元测试之间的区别:好的测试通过及早发现错误并使测试更快来提高开发人员的生产力。糟糕的测试会降低开发人员的工作效率。测试附加功能时,生产力通常会受到影响。当代码更改时测试失败,即使它仍然是正确的。发生这种情况是因为输出不同,但部分原因是它不是函数契约的一部分。因此,一个好的单元测试可以帮助执行函数提交的契约。如果单元测试失败,则意味着违反了合同,应该明确修复(通过更改文档和测试),或修复(通过修复代码并保持测试不变)。虽然将测试限制为仅执行公共合同是一项复杂的学习技能,但有一些工具可以提供帮助。一个这样的工具是Hamcrest,一个用于编写断言的框架。它最初是为基于Java的单元测试而发明的,现在支持多种语言,包括Python。Hamcrest旨在使测试断言更易于编写和更精确。defadd(a,b):returna+bfromhamcrestimportassert_that,equal_todeftest_add():assert_that(add(2,2),equal_to(4))这是一个简单函数的断言。如果我们想断言更复杂的函数怎么办?deftest_set_removal():my_set={1,2,3,4}my_set.remove(3)assert_that(my_set,contains_inanyorder([1,2,4]))assert_that(my_set,is_not(has_item(3)))注意,我们可以简单地断言结果是任意顺序的1、2和4,因为集合不保证顺序。我们也可以很容易地用is_not否定断言。这有助于我们编写精确的断言,使我们能够将自己限制在执行函数的公共契约上。然而,有时,没有一个内置功能是我们真正需要的。在这些情况下,Hamcrest允许我们编写自己的匹配器。想象一下下面的函数:defscale_one(a,b):scale=random.randint(0,5)pick=random.choice([a,b])returnscale*pick我们可以自信地断言它的结果是均匀分布的至少一个输入。matcher继承自hamcrest.core.base_matcher.BaseMatcher,重写了两个方法:(item%self.factor)==0defdescribe_to(self,description):description.append_text('numberdivisibleby')description.append_text(repr(self.factor))写一个高质量的describe_to方法很重要,因为那是测试失败时显示的消息的一部分。defdivisible_by(num):returnDivisibleBy(num)按照惯例,我们将匹配器包装在一个函数中。有时这给了我们进一步处理输入的机会,但在这种情况下,我们不需要进一步处理。deftest_scale():result=scale_one(3,7)assert_that(result,any_of(divisible_by(3),divisible_by(7)))注意,我们将divisible_by匹配器与内置的any_of匹配器结合起来,以确保我们只测试函数提交了什么。在编辑这篇文章时,我听到一个谣言,说之所以选择“Hamcrest”这个名字,是因为它是字母“matches”的变位词。嗯...>>>assert_that("matches",contains_inanyorder(*"hamcrest")Traceback(最近调用最后):文件“”,第1行,在文件“/home/moshez/src/devops-python/build/devops/lib/python3.6/site-packages/hamcrest/core/assert_that.py”,第43行,在assert_that_assert_match(actual=arg1,matcher=arg2,reason=arg3)文件中“/home/moshez/src/devops-python/build/devops/lib/python3.6/site-packages/hamcrest/core/assert_that.py”,第57行,在_assert_matchraiseAssertionError(description)AssertionError:Expected:asequenceover['h','a','m','c','r','e','s','t']任何顺序但是:没有项目匹配:['中的'r'm','a','t','c','h','e','s']进一步研究后,我找到了谣言的来源:它是字母“matchers”的字谜".>>>assert_that("matchers",contains_inanyorder(*"hamcrest"))>>>如果您还没有为Python代码编写单元测试,现在是开始的好时机。如果您正在为您的Python代码编写单元测试,使用Hamcrest将使您的断言更加精确,既不超过也不会低于您想要测试的水平。这将减少修改代码时的误报,并减少修改工作代码测试所花费的时间。