如何用Hypothesis生成含重复字符的字符串优化游程编码往返测试?
优化Hypothesis往返测试策略:自定义Run-Length编码/解码函数
问题背景
需要对自定义的Run-Length编码/解码函数做往返测试(decode(encode(s)) == s 或 encode(decode(l)) == l),但当前用st.text()生成的测试字符串重复字符占比低,无法充分验证编码/解码逻辑的正确性,需要优化Hypothesis生成策略,用one_of生成含随机位置、随机长度重复字符的字符串,或直接生成符合编码格式的列表。
原实现代码
from hypothesis import given, strategies as st from itertools import groupby from typing import List, Union def run_length_encoder(in_string: str) -> List[Union[str, int]]: """ >>> run_length_encoder("aaaaabbcbc") ['a', 'a', 5, 'b', 'b', 2, 'c', 'b', 'c'] """ assert isinstance(in_string, str) out = [] for item, group in groupby(in_string): cnt = sum(1 for x in group) if cnt == 1: out.append(item) else: out.extend((item, item, cnt)) assert isinstance(out, list) assert all(isinstance(x, (str, int)) for x in out) return out def run_length_decoder(in_list: List[Union[str, int]]) -> str: """ >>> run_length_decoder(['a', 'a', 5, 'b', 'b', 2, 'c', 'b', 'c']) "aaaaabbcbc" """ assert isinstance(in_list, list) assert all(isinstance(x, (str, int)) for x in in_list) out: str = "" for item in in_list: if isinstance(item, int): out += out[-1] * (item - 2) else: out += item assert isinstance(out, str) return out
原测试代码
@given( in_string = st.text() ) def test_roundtrip_run_length_encoder_decoder(in_string): encoded_list = run_length_encoder(in_string) assert isinstance(encoded_list, list) assert all(isinstance(x, (str, int)) for x in encoded_list) decoded_string = run_length_decoder(encoded_list) assert isinstance(decoded_string, str) assert in_string == decoded_string, (in_string, decoded_string) test_roundtrip_run_length_encoder_decoder()
优化方案
方案1:生成含随机重复字符的字符串
用st.one_of混合基础文本策略和重复字符块策略,确保测试用例包含足够多的重复场景:
# 生成单个字符+重复次数的元组,次数≥1 repeat_block = st.tuples( st.characters(), st.integers(min_value=1, max_value=20) # 重复次数可根据需求调整 ).map(lambda t: t[0] * t[1]) # 混合基础文本和重复块拼接的文本 text_with_repeats = st.one_of( st.text(), # 保留原有基础文本测试场景 st.lists(repeat_block, min_size=1).map(''.join) # 多个重复块拼接成字符串 ) @given(in_string=text_with_repeats) def test_roundtrip_run_length_encoder_decoder(in_string): encoded_list = run_length_encoder(in_string) decoded_string = run_length_decoder(encoded_list) assert in_string == decoded_string, f"Original: {in_string}, Decoded: {decoded_string}"
方案2:直接生成符合编码格式的列表
针对encode(decode(l)) == l的往返测试,直接构造符合编码规则的列表策略,更精准测试解码后再编码的正确性:
# 生成单个字符项(对应原字符串中出现1次的字符) single_char_item = st.characters().map(lambda c: [c]) # 生成重复项:[char, char, count],count≥2(对应原字符串中出现≥2次的字符) repeat_item = st.tuples( st.characters(), st.integers(min_value=2, max_value=20) ).map(lambda t: [t[0], t[0], t[1]]) # 混合两种项,拼接成符合编码格式的列表 encoded_list_strategy = st.lists( st.one_of(single_char_item, repeat_item), min_size=0 ).map(lambda lst: [item for sublist in lst for item in sublist]) @given(in_list=encoded_list_strategy) def test_roundtrip_run_length_decoder_encoder(in_list): decoded_string = run_length_decoder(in_list) encoded_list = run_length_encoder(decoded_string) assert in_list == encoded_list, f"Original list: {in_list}, Encoded list: {encoded_list}"
说明
- 方案1重点覆盖编码→解码的往返逻辑,确保任意字符串经过编码解码后还原;
- 方案2重点覆盖解码→编码的往返逻辑,确保符合编码格式的列表经过解码编码后还原;
- 两个方案可结合使用,全面验证函数的正确性。
内容的提问来源于stack exchange,提问作者fabio
相关产品推荐
相关产品推荐

