如何为 Plotly Dash 回调函数编写测试用例?
How to write testcases for Plotly Dash callback function?
我正在尝试为页面中的 Dash 回调函数编写一个测试用例,该页面将 2 个数据表的选定行存储到 dash_core_components.Store
,并通过 dash.callback_context
确定状态
index.py
中的示例回调函数
@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
if not ctx.triggered:
return {'row-index-v1': [0], 'row-index-v2': [0]}
else:
return {'row-index-v1': row1, 'row-index-v2': row2}
test.py
中的示例测试用例
def test_store_row_selection_1(app_datatable):
r1 = [0]
r2 = [1]
result = store_row_selection(r1, r2)
assert type(result) is dict
但是,Pytest 在 运行 时抛出异常,测试回调函数的正确方法是什么?我怎样才能让它工作?
@wraps(func)
def add_context(*args, **kwargs):
> output_spec = kwargs.pop("outputs_list")
E KeyError: 'outputs_list'
我建议你测试@app.callback
未修饰的函数。
Dash 在 store_row_selection
函数上使用 functools.wraps
for creating callbacks (source code). This means we have access to the __wrapped__
属性。
__wrapped__
属性为我们提供了原始的底层函数,store_row_selection
在这种情况下:
def test_store_row_selection_1():
r1 = [0]
r2 = [1]
result = store_row_selection.__wrapped__(r1, r2)
assert type(result) is dict
另一种方法是将您的回调代码分成几部分:
def do_stuff(row1, row2):
# Do things
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:
return do_stuff(row1, row2)
并测试不依赖于 Dash 回调的函数(本例中为do_stuff
)。
根据编辑和评论更新
May I know if it is possible to trigger ctx.triggered in pytest as well?
dash.call_context
仅在回调 (source) 中可用,因此 __wrapped__
方法在这种情况下不起作用。另一种方法(拆分函数)会起作用,因为您可以将 ctx
传递给 do_stuff
并在测试中模拟 callback_context
。
我正在尝试为页面中的 Dash 回调函数编写一个测试用例,该页面将 2 个数据表的选定行存储到 dash_core_components.Store
,并通过 dash.callback_context
index.py
@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
if not ctx.triggered:
return {'row-index-v1': [0], 'row-index-v2': [0]}
else:
return {'row-index-v1': row1, 'row-index-v2': row2}
test.py
def test_store_row_selection_1(app_datatable):
r1 = [0]
r2 = [1]
result = store_row_selection(r1, r2)
assert type(result) is dict
但是,Pytest 在 运行 时抛出异常,测试回调函数的正确方法是什么?我怎样才能让它工作?
@wraps(func)
def add_context(*args, **kwargs):
> output_spec = kwargs.pop("outputs_list")
E KeyError: 'outputs_list'
我建议你测试@app.callback
未修饰的函数。
Dash 在 store_row_selection
函数上使用 functools.wraps
for creating callbacks (source code). This means we have access to the __wrapped__
属性。
__wrapped__
属性为我们提供了原始的底层函数,store_row_selection
在这种情况下:
def test_store_row_selection_1():
r1 = [0]
r2 = [1]
result = store_row_selection.__wrapped__(r1, r2)
assert type(result) is dict
另一种方法是将您的回调代码分成几部分:
def do_stuff(row1, row2):
# Do things
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:
return do_stuff(row1, row2)
并测试不依赖于 Dash 回调的函数(本例中为do_stuff
)。
根据编辑和评论更新
May I know if it is possible to trigger ctx.triggered in pytest as well?
dash.call_context
仅在回调 (source) 中可用,因此 __wrapped__
方法在这种情况下不起作用。另一种方法(拆分函数)会起作用,因为您可以将 ctx
传递给 do_stuff
并在测试中模拟 callback_context
。