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

如何用pytest测试含SQL调用的POST请求?解决表不存在报错

问题分析与解决方案

问题核心

你遇到的“表不存在”错误本质是测试环境与生产环境使用的数据库不一致:正常运行时用的是MySQL,而pytest测试时配置了SQLite(sqlite:////tmp/test.db),且你mock了create_all方法,导致SQLite中从未创建过movies表。


待测试代码与配置回顾

业务逻辑与接口

def getMovieBudget(movieId):
    engine = create_engine(app.config['SQLALCHEMY_DATABASE_URI'])
    query = """
            SELECT *
            FROM movies
            WHERE id = '{}';
            """.format(movieId)
    data = pd.read_sql(query, engine)
    return data.iloc[0]['building_id']

@app.route('/upload', methods=['POST'])
def test_post():
    # .. 其他逻辑
    movieId = request.form['movieId']
    movieBudget = getMovieBudget(movieId)

测试代码

def test_post(client, capsys):
    url = f'/{helpers.get_domain()}/{helpers.get_version()}/movies/'

    data = {
        'movieId': 144,
    }

    response = client.post(url, data=data)

    assert response.status_code == 200

测试配置

os.environ['TEST_ENV'] = 'TRUE'

@pytest.fixture(autouse=True)
def mock_patch(monkeypatch):
    monkeypatch.setattr(SQLAlchemy, "create_all", mock.Mock(return_value=True))

@pytest.fixture
def client():
    from movie_handler import app

    app.config['TESTING'] = True
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
    return app.test_client()

解决办法

方法1:初始化SQLite测试表

取消对create_all的mock,让SQLAlchemy自动创建表(若用ORM),或手动执行建表语句(原生SQL场景):

场景A:使用SQLAlchemy ORM

# 删除或注释掉mock create_all的fixture
# @pytest.fixture(autouse=True)
# def mock_patch(monkeypatch):
#     monkeypatch.setattr(SQLAlchemy, "create_all", mock.Mock(return_value=True))

@pytest.fixture
def client():
    from movie_handler import app, db  # 引入你的SQLAlchemy db实例

    app.config['TESTING'] = True
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
    
    # 在测试前创建表
    with app.app_context():
        db.create_all()
    
    yield app.test_client()
    
    # 测试完成后清理表
    with app.app_context():
        db.drop_all()

场景B:使用原生SQL

@pytest.fixture
def client():
    from movie_handler import app
    from sqlalchemy import create_engine

    app.config['TESTING'] = True
    db_uri = 'sqlite:////tmp/test.db'
    app.config['SQLALCHEMY_DATABASE_URI'] = db_uri
    
    # 手动创建movies表并插入测试数据
    engine = create_engine(db_uri)
    with engine.connect() as conn:
        conn.execute("""
            CREATE TABLE IF NOT EXISTS movies (
                id VARCHAR PRIMARY KEY,
                building_id VARCHAR
                -- 补充其他必要字段
            )
        """)
        conn.execute("INSERT INTO movies (id, building_id) VALUES ('144', 'b123')")
        conn.commit()
    
    yield app.test_client()
    
    # 测试后清理
    with engine.connect() as conn:
        conn.execute("DROP TABLE movies")
        conn.commit()

方法2:Mock子函数,跳过真实数据库调用

如果测试重点是验证POST接口的参数处理、流程逻辑,而非SQL查询本身,直接mockgetMovieBudget:

def test_post(client, monkeypatch):
    # 替换原函数为mock实现
    def mock_getMovieBudget(movieId):
        return 'b123'
    
    monkeypatch.setattr('movie_handler.getMovieBudget', mock_getMovieBudget)
    
    url = f'/{helpers.get_domain()}/{helpers.get_version()}/movies/'
    data = {'movieId': 144}
    
    response = client.post(url, data=data)
    
    assert response.status_code == 200

方法3:测试环境使用MySQL(不推荐)

将测试配置改为使用真实MySQL数据库,但需注意测试数据隔离:

@pytest.fixture
def client():
    from movie_handler import app

    app.config['TESTING'] = True
    # 替换为你的测试MySQL连接串
    app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://username:password@localhost/test_movie_db'
    return app.test_client()

注意:必须使用专门的测试数据库,测试前后清理数据,避免污染业务数据。


内容的提问来源于stack exchange,提问作者teleluck

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 13:09:26