Sherlock单元测试编写指南:从基础断言到复杂场景模拟
单元测试框架概览
Sherlock项目采用pytest作为单元测试框架,测试代码集中在tests/目录下。该目录包含多个测试模块,覆盖从基础功能验证到复杂场景模拟的完整测试体系:
- 基础测试:test_version.py验证版本号管理
- 核心功能:test_probes.py实现探针逻辑测试
- 用户交互:test_ux.py处理命令行参数解析
- 数据验证:test_manifest.py校验站点配置文件
基础测试用例编写
简单断言测试
最基础的测试形式是直接验证函数返回值,如test_version.py中的版本号验证:
def test_versioning() -> None:
"""Test version string handling."""
# 此处省略实际实现
assert True, "版本号验证通过"
参数化测试
使用@pytest.mark.parametrize装饰器可以批量测试不同输入组合,如test_probes.py中的多场景验证:
@pytest.mark.parametrize('site,username',[
('GitHub', 'ppfeister'),
('GitHub', 'sherlock-project'),
('容器平台', 'ppfeister'),
])
def test_known_positives_via_status_code(self, sites_info, site, username):
assert simple_query(sites_info=sites_info, site=site, username=username) is QueryStatus.CLAIMED
复杂场景模拟技术
随机数据生成
对于边界测试,可生成随机数据验证系统容错能力。以下是test_probes.py中生成超长用户名的实现:
def test_likely_negatives_via_status_code(self, sites_info, site, random_len):
num_attempts: int = 3
attempted_usernames: list[str] = []
status: QueryStatus = QueryStatus.CLAIMED
for i in range(num_attempts):
acceptable_types = string.ascii_letters + string.digits
random_handle = ''.join(random.choice(acceptable_types) for _ in range(random_len))
attempted_usernames.append(random_handle)
status = simple_query(sites_info=sites_info, site=site, username=random_handle)
if status is QueryStatus.AVAILABLE:
break
assert status is QueryStatus.AVAILABLE, f"尝试 {num_attempts} 次后仍无法验证可用用户名"
正则表达式验证
站点用户名格式验证通过正则表达式实现,test_probes.py中的非法字符测试示例:
def test_username_illegal_regex(sites_info):
site: str = 'BitBucket'
invalid_handle: str = '*#$Y&*JRE'
pattern = re.compile(sites_info[site]['regexCheck'])
# 先验证正则表达式本身能捕获非法用户名
assert pattern.match(invalid_handle) is None
# 再验证系统正确返回非法状态
assert simple_query(sites_info=sites_info, site=site, username=invalid_handle) is QueryStatus.ILLEGAL
测试环境配置
夹具(Fixture)设计
conftest.py提供了测试环境的核心配置,包括:
@pytest.fixture
def sites_info():
"""提供站点信息字典的夹具"""
return fetch_local_manifest()
@pytest.fixture
def sites_obj():
"""提供站点对象的夹具"""
from sherlock_project.sites import Sites
return Sites(data=fetch_local_manifest())
测试标记
使用自定义标记对测试进行分类,如test_probes.py中的在线测试标记:
@pytest.mark.online
class TestLiveTargets:
"""对真实目标执行活动探针测试"""
# 测试方法实现...
测试执行与结果分析
运行测试套件
通过项目根目录的tox.ini配置测试环境,执行:
tox
测试覆盖率
结合pytest-cov插件生成覆盖率报告,配置在pytest.ini中:
[pytest]
addopts = --cov=sherlock_project --cov-report=term-missing
testpaths = tests
高级测试模式
模拟网络请求
对于API交互测试,可使用responses库模拟HTTP响应,示例架构:
def test_api_rate_limit():
with responses.RequestsMock() as rsps:
rsps.add(responses.GET, 'https://api.example.com/user',
status=429, json={'error': 'rate limited'})
# 测试代码实现...
行为驱动测试
通过test_ux.py实现用户行为模拟:
def test_wildcard_username_expansion():
"""测试通配符用户名扩展功能"""
# 此处省略实际实现
assert True, "通配符扩展测试通过"
测试最佳实践
测试组织原则
- 单一职责:每个测试函数只验证一个行为
- 可重复性:测试应在任何环境下产生相同结果
- 边界覆盖:如test_probes.py中的超长用户名测试
- 文档化测试:测试用例应自文档化,清晰表达验证意图
常见问题解决
- 测试速度慢:使用pytest-xdist实现并行测试
- 外部依赖:通过模拟隔离外部服务,如test_manifest.py中的本地模式
- 脆弱测试:避免过度指定断言,关注行为而非实现细节
总结
Sherlock的测试架构展示了从简单断言到复杂场景模拟的完整测试策略。通过合理组织测试代码(tests/)、使用参数化测试、设计灵活夹具,确保了项目在迭代过程中的代码质量。开发人员可参考test_probes.py中的模式扩展新的测试场景,或通过test_ux.py添加新的用户交互测试。
完整测试策略文档可参考docs/CODE_OF_CONDUCT.md中的开发规范章节。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考




