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

如何正确编写Plotly Dash回调函数的测试用例?

报错根因

你直接调用被@app.callback装饰后的函数时触发的报错。@app.callback会给原函数包一层上下文处理逻辑,运行时需要框架自动注入outputs_list等内部参数,你手动调用时没有传入这些参数,自然就抛出KeyError: 'outputs_list'。

解决方案

下面是两种最常用的测试方案,按推荐优先级排序:

方案1:拆分业务逻辑与回调装饰(最推荐)

把回调里的业务逻辑抽成无依赖的纯函数,回调本身只做上下文读取和纯函数调用,测试时直接测纯函数即可,完全不需要适配Dash的规则:

  1. 调整index.py代码:
# 抽离的纯业务逻辑,无任何Dash依赖
def process_row_selection(row1: list, row2: list, is_triggered: bool) -> dict:
    if not is_triggered:
        return {'row-index-v1': [0], 'row-index-v2': [0]}
    return {'row-index-v1': row1, 'row-index-v2': row2}

@app.callback(
    Output('rows-store', 'data'),
    Input('datatable1', 'selected_rows'),
    Input('datatable2', 'selected_rows'),
)
def store_row_selection(row1: list, row2: list) -> dict:
    ctx = dash.callback_context
    return process_row_selection(row1, row2, bool(ctx.triggered))
  1. 编写测试用例:
# 直接测试纯函数,不需要任何Dash相关的fixture
def test_store_row_selection():
    # 测试回调被触发的场景
    r1 = [0]
    r2 = [1]
    result = process_row_selection(r1, r2, is_triggered=True)
    assert isinstance(result, dict)
    assert result['row-index-v1'] == [0]
    assert result['row-index-v2'] == [1]

    # 测试初始未触发的场景
    init_result = process_row_selection(r1, r2, is_triggered=False)
    assert init_result == {'row-index-v1': [0], 'row-index-v2': [0]}

方案2:直接调用原始函数+模拟上下文

如果不想改原有代码结构,可以通过__wrapped__属性拿到被装饰前的原始函数,同时模拟dash.callback_context的值来测试:
测试用例示例:

def test_store_row_selection(monkeypatch):
    r1 = [0]
    r2 = [1]

    # 模拟回调被触发的场景
    mock_ctx = type('MockCtx', (), {'triggered': [{'prop_id': 'datatable1.selected_rows'}]})()
    monkeypatch.setattr('dash.callback_context', mock_ctx)
    # 调用被装饰前的原始函数
    result = store_row_selection.__wrapped__(r1, r2)
    assert isinstance(result, dict)
    assert result['row-index-v1'] == [0]

    # 模拟初始未触发的场景
    mock_ctx_no_trigger = type('MockCtx', (), {'triggered': []})()
    monkeypatch.setattr('dash.callback_context', mock_ctx_no_trigger)
    init_result = store_row_selection.__wrapped__(r1, r2)
    assert init_result == {'row-index-v1': [0], 'row-index-v2': [0]}

如果需要测试完整的交互流程,可以用Dash官方的dash.testing工具启动服务模拟用户操作,不过单元测试场景不需要用到这种重方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.20 15:15:04