You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 12:34:58