python 使用 faker 进行假设单元测试

python hypothesis unit test using faker

我在使用假设附加包 Faker 进行 python 单元测试时遇到问题。 我想测试我网站的登录过程,我已经有了单元测试场景,但我想用 hypothesis 自动化场景。 这是我用于验证电子邮件和密码的简单代码。

import unittest

from Environtments.ParametrizedTestCase import ParametrizedTestCase
from hypothesis import given
from faker import Faker

class TestLogin(ParametrizedTestCase):
    fake = Faker('en_US')
    @given(email = fake.email(),password = fake.password()
    def test_login(self,email,password):
        assert '@' in email
        assert len(password) >= 6

if __name__ == '__main__':
    suite = unittest.TestSuite()
    suite.addTest(ParametrizedTestCase.parametrize(TestLogin, param=""))
    unittest.TextTestRunner(verbosity=2).run(suite)

我总是发现这个错误。我错过了什么吗?

test_login (__main__.TestLogin) ... You can add @seed(311936867547523412638507752560457398354) to this test to reproduce this failure.
ERROR

======================================================================
ERROR: test_login (__main__.TestLogin)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tester_uf_exmp_hypo.py", line 10, in test_login
    def test_login(self,email,password):
  File "/Library/Python/2.7/site-packages/hypothesis/core.py", line 1001, in wrapped_test
    state.run()
  File "/Library/Python/2.7/site-packages/hypothesis/core.py", line 725, in run
    runner.run()
  File "/Library/Python/2.7/site-packages/hypothesis/internal/conjecture/engine.py", line 435, in run
    self._run()
  File "/Library/Python/2.7/site-packages/hypothesis/internal/conjecture/engine.py", line 771, in _run
    self.reuse_existing_examples()
  File "/Library/Python/2.7/site-packages/hypothesis/internal/conjecture/engine.py", line 626, in reuse_existing_examples
    self.test_function(last_data)
  File "/Library/Python/2.7/site-packages/hypothesis/internal/conjecture/engine.py", line 153, in test_function
    self._test_function(data)
  File "/Library/Python/2.7/site-packages/hypothesis/core.py", line 691, in evaluate_test_data
    escalate_hypothesis_internal_error()
  File "/Library/Python/2.7/site-packages/hypothesis/core.py", line 663, in evaluate_test_data
    result = self.execute(data, collect=True)
  File "/Library/Python/2.7/site-packages/hypothesis/core.py", line 578, in execute
    result = self.test_runner(data, run)
  File "/Library/Python/2.7/site-packages/hypothesis/executors.py", line 58, in default_new_style_executor
    return function(data)
  File "/Library/Python/2.7/site-packages/hypothesis/core.py", line 552, in run
    args, kwargs = data.draw(self.search_strategy)
  File "/Library/Python/2.7/site-packages/hypothesis/internal/conjecture/data.py", line 158, in draw
    return self.__draw(strategy, label=label)
  File "/Library/Python/2.7/site-packages/hypothesis/internal/conjecture/data.py", line 175, in __draw
    return strategy.do_draw(self)
  File "/Library/Python/2.7/site-packages/hypothesis/core.py", line 180, in do_draw
    return self.base.do_draw(data)
  File "/Library/Python/2.7/site-packages/hypothesis/searchstrategy/lazy.py", line 157, in do_draw
    return data.draw(self.wrapped_strategy)
  File "/Library/Python/2.7/site-packages/hypothesis/internal/conjecture/data.py", line 148, in draw
    if strategy.is_empty:
  File "/Library/Python/2.7/site-packages/hypothesis/searchstrategy/strategies.py", line 144, in accept
    recur(self)
  File "/Library/Python/2.7/site-packages/hypothesis/searchstrategy/strategies.py", line 141, in recur
    mapping[strat] = getattr(strat, calculation)(recur)
  File "/Library/Python/2.7/site-packages/hypothesis/searchstrategy/collections.py", line 67, in calc_is_empty
    return any(recur(e) for e in self.element_strategies)
  File "/Library/Python/2.7/site-packages/hypothesis/searchstrategy/collections.py", line 67, in <genexpr>
    return any(recur(e) for e in self.element_strategies)
  File "/Library/Python/2.7/site-packages/hypothesis/searchstrategy/strategies.py", line 141, in recur
    mapping[strat] = getattr(strat, calculation)(recur)
  File "/Library/Python/2.7/site-packages/hypothesis/searchstrategy/strategies.py", line 541, in calc_is_empty
    return recur(self.mapped_strategy)
  File "/Library/Python/2.7/site-packages/hypothesis/searchstrategy/strategies.py", line 141, in recur
    mapping[strat] = getattr(strat, calculation)(recur)
  File "/Library/Python/2.7/site-packages/hypothesis/searchstrategy/lazy.py", line 87, in calc_is_empty
    return recur(self.wrapped_strategy)
  File "/Library/Python/2.7/site-packages/hypothesis/searchstrategy/lazy.py", line 110, in wrapped_strategy
    *self.__args, **self.__kwargs
  File "/Library/Python/2.7/site-packages/hypothesis/strategies.py", line 651, in fixed_dictionaries
    check_strategy(v)
  File "/Library/Python/2.7/site-packages/hypothesis/internal/validation.py", line 45, in check_strategy
    check_type(SearchStrategy, arg, name)
  File "/Library/Python/2.7/site-packages/hypothesis/internal/validation.py", line 39, in check_type
    % (typ_string, name, arg, type(arg).__name__))
InvalidArgument: Expected SearchStrategy but got u'2)Y4+APqu@' (type=unicode)

----------------------------------------------------------------------
Ran 1 test in 2.013s

FAILED (errors=1)

我把代码改成这样。

from hypothesis.extra.fakefactory import fake_factory    
@given(email = fake_factory('email'),password = fake_factory('password'))

这是警告。但是伪造者有效。这会有问题吗?

tester_uf_exmp_hypo.py:9: HypothesisDeprecationWarning: hypothesis.extra.fakefactory has been deprecated, because it does not support example discovery or shrinking.  Consider using a lower-level strategy (such as st.text()) instead.
  @given(email = fake_factory('email'),password = fake_factory('password'))
test_login (__main__.TestLogin) ... kaareberge@pedersen.no
!l3QCGDd8I
myslrshd@bn.com
(@s(RwUM0z

您的第一个案例是使用错误类型的参数调用 @given - 假设要求它们是 "strategies" 以生成示例值,而不是值本身。您可以在回溯的底部看到这一点 - got u'2)Y4+APqu@' (type=unicode)fake.password() 的结果,而不是(例如)hypothesis.strategies.text().

第二个代码片段确实有效 - 欢呼!

但是 - 这就是我写弃用警告的原因 - 它放弃了假设的大部分优点:因为这只是 fake-factory 的包装器,无法最小化示例并且测试过程不会没有足够的信息来进行 coverage-guided 优化。

相反,您可以考虑密码 是什么 - 在这种情况下,也许是 "a unicode string with between 6 and 40 characters"。在假设中,那将是:

import hypothesis.strategies as st

@given(password=st.text(min_size=6, max_size=40))
def test(password): 
    ...

然后你会缩小,profile-guided 示例选择,还有更多 :D

"Email addresses" 也是字符串,但具有更多结构 - 幸运的是,Hypothesis 也涵盖了这一点。例如,您可以使用 .map() 的部分组成策略,或者使用 from_regex() 策略,或者只是从 hypothesis.provisional.emails 复制我们的内部实现(或者甚至只是导入它,如果你不请注意可能随时删除的不稳定 API)。

TLDR:您可以混合使用 Hypothesis 和 fake-factory,但我只使用 Hypothesis。