首页 > 解决方案 > 使用 testbook 测试 Jupyter notebook 单元时如何修补 input()?

问题描述

我参与的一个项目使用testbook来测试 Jupyter 笔记本的代码单元。修补工作正常——除非要测试的代码要求用户输入input(). 我只是不知道如何正确修补它。

使用的版本:Python:3.8.10,testbook:0.4.2

要在 Jupyter 代码单元中测试的代码,标记为name_checking

def fix(text):
    return text.strip().title()

def check(text):
    return len(text) > 1

firstname = input("What's your first name?")
lastname = input("What's your last name?")
fixed_first = fix(firstname)
fixed_last = fix(lastname)
if check(fixed_first) and check(fixed_last):
    print(f"Your name is {fixed_first} {fixed_last}.")
else:
    print("You entered an invalid name.")

尝试1:测试代码补丁builtins.input

@testbook(path)
def test_name_checking1(tb): # execute cell tagged "name_checking"
    with tb.patch("builtins.input") as mock_input:
        mock_input.return_value = ["   haRrY   ", "  pOtter "]
        result = tb.execute_cell("name_checking")
        assert tb.cell_output_text("name_checking") == "Harry"

这失败了:

E                     8 
E               ----> 9 firstname = input("What's your first name?")
E                    10 lastname = input("What's your last name?")
E                    11 
E               
E               ~/proj/.venv/lib/python3.8/site-packages/ipykernel/kernelbase.py in raw_input(self, prompt)
E                   976         """
E                   977         if not self._allow_stdin:
E               --> 978             raise StdinNotImplementedError(
E                   979                 "raw_input was called, but this frontend does not support input requests."
E                   980             )
E               
E               StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.
E               StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.
../.venv/lib/python3.8/site-packages/testbook/client.py:135: TestbookRuntimeError

尝试2:测试代码补丁ipykernel.kernelbase.Kernel.raw_input

@testbook(path)
def test_name_checking2(tb):
    with tb.patch("ipykernel.kernelbase.Kernel.raw_input") as mock_input:
        mock_input.return_value = ["   haRrY   ", "  pOtter "]
        result = tb.execute_cell("name_checking")
        firstname = tb.ref("firstname")
        assert tb.cell_output_text("name_checking") == "Harry"

这失败了:

tb = <testbook.client.TestbookNotebookClient object at 0x7f74aaca2af0>

    @testbook(path)
    def test_name_checking2(tb): # execute cell tagged "name_checking"
        with tb.patch("ipykernel.kernelbase.Kernel.raw_input") as mock_input:
            mock_input.return_value = ["   haRrY   ", "  pOtter "]
            result = tb.execute_cell("name_checking")
            firstname = tb.ref("firstname")
            print(firstname)
>           assert tb.cell_output_text("name_checking") == "Harry"
E           AssertionError: assert 'You entered an invalid name.' == 'Harry'
E             - Harry
E             + You entered an invalid name.

03-functions/tests/test_name_checking.py:33: AssertionError
----------------------------------------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------------------------------------
"<MagicMock name='raw_input()' id='139650606478576'>"

有谁知道必须修补什么或如何正确修补input()

标签: pythontestingjupyter-notebookmonkeypatchingtestbook

解决方案


我通过深入研究 testbook 的代码并了解有关修补的更多信息找到了解决方案:

  • 修补ipykernel.kernelbase.Kernel.raw_input是正确的方法

  • 但是:提供input()使用side_effect参数的返回值,而不是mock_input.return_value

正确的测试代码:

@testbook(path)
def test_name_checking(tb):
    with tb.patch(
        "ipykernel.kernelbase.Kernel.raw_input",
        side_effect=["   haRrY   ", "  pOtter "],
    ) as mock_input:
        tb.execute_cell("name_checking")
        assert tb.cell_output_text("name_checking") == "Your name is Harry Potter."

推荐阅读