'How to patch input() when testing Jupyter notebook cells with testbook?
A project I'm involved in uses testbook to test code cells of Jupyter notebooks. Patching works fine — unless the code to be tested asks for user input with input()
. I just can't figure out how to patch it correctly.
Versions used: Python: 3.8.10, testbook: 0.4.2
Code to be tested in a Jupyter code cell, tagged as 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.")
Attempt 1: The test code patches 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"
This fails with:
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
Attempt 2: The test code patches 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"
This fails with:
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'>"
Does anyone have an idea what has to be patched or how to correctly patch input()
?
Solution 1:[1]
I found the solution by digging into testbook's code and learning more about patching:
Patching
ipykernel.kernelbase.Kernel.raw_input
is the way to go and correctbut: Provide return values from
input()
using theside_effect
argument, notmock_input.return_value
Correct test code:
@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."
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | das-g |