为了账号安全,请及时绑定邮箱和手机立即绑定

如何使用 Pytest 和 Hypothesis 控制随机种子?

如何使用 Pytest 和 Hypothesis 控制随机种子?

守着星空守着你 2023-05-16 14:56:25
我有一个测试执行一个使用随机事物的函数。我想使用假设(或其他东西?)来运行它几次并知道,当它失败时,使用了哪个随机种子。我怎样才能做到这一点?我的目标是多次测试我的代码,以确保它不会因为使用随机数而失败。
查看完整描述

3 回答

?
PIPIONE

TA贡献1829条经验 获得超9个赞

是的,假设听起来是个好方法。例如:


from unittest import TestCase

from hypothesis import given

from hypothesis.strategies import integers

import hypothesis_random as undertest



class Test(TestCase):

    @given(seed=integers())

    def test_uses_random(self, seed):

        undertest.uses_random(seed)

如果您的函数引发错误,您将获得异常的回溯和来自假设的伪造示例,该示例触发它作为测试的输出,例如


Falsifying example: test_uses_random(

    self=<test_hypothesis_random.Test testMethod=test_uses_random>, seed=-43,

)


Error

Traceback (most recent call last):

...


查看完整回答
反对 回复 2023-05-16
?
慕尼黑的夜晚无繁华

TA贡献1864条经验 获得超6个赞

假设对于您的用例来说是一种极好的可能性——如果您使用得当的话。首先,为什么它有效:它不是随机的,而是伪随机的。当一个复杂示例的测试失败时,它会降低复杂性,直到找到失败的最小测试用例,然后给你那个。然后它还存储失败测试用例的数据库,因此重放旧失败是它尝试的第一件事。


现在,缺点是构建测试用例通常需要很长时间,但好处是您可以真正确定您的代码是健壮的。


我不知道你的代码是什么样的,只是为了给你一个模型:


from hypothesis import strategies as st

from hypothesis import assume


# In this example, damage and miss chance are based

# on the length of the name of the attack

samus = Character(health=100, attacks=['punch', 'shoot'])

wario = Character(health=70, attacks=['growl', 'punch'])

bowser = Character(health=250, attacks=['growl', 'kidnap_princess'])


st_character = st.sampled_from([samus, wario, bowser])

st_n_rounds = st.integer(min=0, max=10)


@st.composite

def fight_sequence(draw):

  player_one = draw(st_character)

  player_two = draw(st_character)


  # don't test entire fights, just simulate one, record the steps,

  # and check that the end state is what you expect

  actions = [

    dict(type='choose', player_number=1, player=player_one),

    dict(type='choose', player_number=2, player=player_two)

  ]


  # this filters out all test cases where players have the same character

  assume(player_one != player_two)


  n_rounds = draw(st_n_rounds)

  both_alive = True


  def _attack(player, other):

    if not both_alive:

      return


    attack = draw(player.attacks)

    response = draw(st.integers(min=0, max=len(attack)))

    response_type = 'miss' if response == 0 else 'crit' if response == len(attack)) else 'hit'

    actions.push(dict(type='attack', player=player, attack=attack, response=response_type))


    if response_type == 'hit':

       other.health -= len(attack)

    elif response_type == 'crit':

       other.health -= len(attack) * 2


    if other.health <= 0:

      actions.push(dict(type='ko', player=other))


  for _ in range(n_rounds):

    _attack(player_one, player_two)

    _attack(player_two, player_one)

  return actions

然后在您的测试用例中,将回放脚本提供给您的代码并检查结果是否一致。我希望你能以此为灵感。


查看完整回答
反对 回复 2023-05-16
?
慕丝7291255

TA贡献1859条经验 获得超6个赞

Hypothesis 的st.random_module()策略正是为这个用例而设计的。



查看完整回答
反对 回复 2023-05-16
  • 3 回答
  • 0 关注
  • 123 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信