FastAPI接口抛出HTTPException但CodeCov显示未覆盖,测试已通过
问题:FastAPI登录接口分支覆盖率未被CodeCov识别
我为FastAPI的/login接口编写了两个测试用例:一个验证有效凭证登录成功,另一个验证无效凭证返回401,所有测试均执行通过。但CodeCov显示if not user:及后续抛出HTTPException的代码分支未被覆盖。我尝试了本地运行pytest生成覆盖率报告、用pytest.raises捕获异常的方法,均未解决问题。
相关代码
接口代码
@router.post("/login", response_model=schemas.TokenResponse) async def login_for_access_token( form_data: OAuth2PasswordRequestForm = Depends(), user_interactor: UserInteractor = Depends(get_user_interactor), token_interactor: TokenInteractor = Depends(get_token_interactor), config: AppConfig = Depends(get_config), security_service: SecurityService = Depends(get_security_service) ): # If user with given username/pass found - returns user object, # if not found due to any reason - None user = await user_interactor.verify_user_password(form_data.username, form_data.password) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password", headers={"WWW-Authenticate": "Bearer"}, ) # .. some logic .. return schemas.TokenResponse(..)
测试代码
async def test_login_user(client: AsyncClient, test_user): response = await client.post( "/api/v1/auth/login", data={"username": test_user.username, "password": "testpassword"} ) assert response.status_code == 200 assert "access_token" in response.json() assert "refresh_token" in response.json() assert response.json()["token_type"] == "bearer" async def test_login_invalid_credentials(client: AsyncClient): response = await client.post( "/api/v1/auth/login", data={"username": "nonexistentuser", "password": "wrongpassword"} ) assert response.status_code == 401 assert "Incorrect username or password" in response.json()["detail"] assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Bearer"
verify_user_password实现
async def verify_user_password( self, username: str, password: str ) -> Optional[schemas.User]: user: Optional[UoWModel] = await self.user_gateway.get_by_username(username) if not user: return None if await self.user_gateway.verify_password(user, password, self.security_service): return schemas.User.model_validate(user._model) return None
解决方案
1. 确认测试确实触发了目标分支
在接口的if not user:分支中添加临时打印语句,运行test_login_invalid_credentials测试,检查是否有输出:
if not user: print("进入无效凭证分支") # 临时添加 raise HTTPException(...)
如果没有输出,说明测试未实际触发该分支,大概率是依赖注入问题:
- 检查测试用的
client是否使用了真实数据库,而数据库中存在nonexistentuser(需排除此极端情况) - 确认
user_interactor的实现/mock是否在传入无效凭证时确实返回None
2. 修正覆盖率统计路径
确保pytest-cov正确覆盖接口文件:
- 在
pytest.ini或命令行中指定正确的源码路径,比如:[pytest] addopts = --cov=your_project_name --cov-report=xml - 本地生成HTML格式的覆盖率报告(
pytest --cov-report=html),直接查看目标接口的if not user:分支是否被标记为已覆盖。如果本地报告显示已覆盖但CodeCov没识别,问题出在CI的CodeCov上传配置。
3. 添加单元测试直接触发分支
集成测试(用AsyncClient)可能因依赖链复杂导致覆盖率统计遗漏,直接编写单元测试调用接口函数,强制mock依赖返回None:
from unittest.mock import Mock from fastapi import HTTPException, status from your_module.path import login_for_access_token async def test_login_trigger_401_branch(): # Mock所有依赖项 form_data = Mock(username="fake_user", password="fake_pass") user_interactor = Mock() # 强制让verify_user_password返回None,触发目标分支 user_interactor.verify_user_password.return_value = None token_interactor = Mock() config = Mock() security_service = Mock() # 捕获异常,验证分支逻辑 try: await login_for_access_token( form_data=form_data, user_interactor=user_interactor, token_interactor=token_interactor, config=config, security_service=security_service ) assert False, "未抛出预期的HTTPException" except HTTPException as exc: assert exc.status_code == status.HTTP_401_UNAUTHORIZED assert exc.detail == "Incorrect username or password" assert exc.headers == {"WWW-Authenticate": "Bearer"}
4. 同步本地与CI环境的依赖版本
检查CI中使用的pytest、pytest-cov、codecov版本是否与本地一致,版本差异可能导致覆盖率统计结果不同。
内容的提问来源于stack exchange,提问作者MaxiMaxi
相关产品推荐
相关产品推荐

