pyhamcrest配置

测试金字塔的基础是单元测试。 单元测试一次测试一个代码单元-通常是一个功能或方法。

通常,单个单元测试旨在测试通过功能或特定分支选择的一个特定流。 这样可以轻松映射失败的单元测试和导致失败的错误。

理想情况下,单元测试使用很少或不使用外部资源,从而将它们隔离开来并使其更快。

好的测试可以通过尽早发现错误并加快测试速度来提高开发人员的生产力。 不良测试会降低开发人员的工作效率。

测试附带特征时,生产率通常会降低。 即使代码仍然正确,测试也会在代码更改时失败。 发生这种情况是因为输出是不同的,但不是函数合同的一部分。

因此,良好的单元测试可以帮助强制执行该功能所遵循的合同。

如果单元测试失败,则将违反合同,并应进行明确修改(通过更改文档和测试),或进行修订(通过修改代码并保持测试不变)。

虽然限制测试以仅强制执行公共合同是一项学习的复杂技能,但有些工具可以提供帮助。

这些工具之一是Hamcrest ,这是一个编写断言的框架。 Hamcrest框架最初是为基于Java的单元测试而发明的,如今它支持多种语言,包括Python

Hamcrest旨在使测试断言更易于编写和更加精确。


   
def add ( a , b ) :
    return a + b

from hamcrest import assert_that , equal_to

def test_add ( ) :
    assert_that ( add ( 2 , 2 ) , equal_to ( 4 ) )  

对于简单的功能,这是一个简单的断言。 如果我们想断言更复杂的事情怎么办?


   
def test_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 ) ) )

请注意,我们可以简洁地断言,结果有124以任意顺序,因为集不保证秩序。

我们还可以轻松地使用is_not否定断言。 这有助于我们编写精确的断言 ,从而使我们可以将自己限制为执行公共职能合同。

但是,有时候,内置功能正是我们所需要的。 在这种情况下,Hamcrest允许我们编写自己的匹配器。

想象一下以下功能:


   
def scale_one ( a , b ) :
    scale = random . randint ( 0 , 5 )
    pick = random . choice ( [ a , b ] )
    return scale * pick

我们可以放心地断言,结果将平均分成至少一个输入。

匹配器继承自hamcrest.core.base_matcher.BaseMatcher ,并覆盖两个方法:


   
class DivisibleBy ( hamcrest. core . base_matcher . BaseMatcher ) :

    def __init__ ( self , factor ) :
        self . factor = factor

    def _matches ( self , item ) :
        return ( item % self . factor ) == 0

    def describe_to ( self , description ) :
        description. append_text ( 'number divisible by' )
        description. append_text ( repr ( self . factor ) )

编写高质量的describe_to方法很重要,因为这是测试失败时将显示的消息的一部分。


   
def divisible_by ( num ) :
    return DivisibleBy ( num )

按照惯例,我们将匹配器包装在一个函数中。 有时,这使我们有机会进一步处理输入,但是在这种情况下,不需要进一步处理。


   
def test_scale ( ) :
    result = scale_one ( 3 , 7 )
    assert_that ( result ,
                any_of ( divisible_by ( 3 ) ,
                       divisible_by ( 7 ) ) )

请注意,我们结合了divisible_by与匹配内置any_of匹配,以确保我们只测试什么的合同提交给。

在编辑本文时,我听说有传言称“ Hamcrest”被选为“比赛”的字谜。 嗯...


   
>>> assert_that ( "matches" , contains_inanyorder ( * "hamcrest" )
Traceback ( most recent call last ) :
  File "<stdin>" , line 1 , in < module >
  File "/home/moshez/src/devops-python/build/devops/lib/python3.6/site-packages/hamcrest/core/assert_that.py" , line 43 , in assert_that
    _assert_match ( actual = arg1 , matcher = arg2 , reason = arg3 )
  File "/home/moshez/src/devops-python/build/devops/lib/python3.6/site-packages/hamcrest/core/assert_that.py" , line 57 , in _assert_match
    raise AssertionError ( description )
AssertionError :
Expected: a sequence over [ 'h' , 'a' , 'm' , 'c' , 'r' , 'e' , 's' , 't' ] in any order
      but: no item matches: 'r' in [ 'm' , 'a' , 't' , 'c' , 'h' , 'e' , 's' ]

经过深入研究,我发现了谣言的来源:这是“匹配器”的字谜。


   
>>> assert_that ( "matchers" , contains_inanyorder ( * "hamcrest" ) )
>>>

如果您尚未为Python代码编写单元测试,那么现在是开始的好时机。 如果您要为Python代码编写单元测试,则使用Hamcrest将使您的断言更加精确 -既不多于或不小于您要测试的内容。 修改代码时,这将导致更少的误报,并减少为工作代码修改测试所花费的时间。

翻译自: https://opensource.com/article/18/8/robust-unit-tests-hamcrest

pyhamcrest配置

更多推荐