#python #unit-testing #python-3.x #jupyter-notebook
#python #модульное тестирование #python-3.x #jupyter-записная книжка
Вопрос:
Я бы хотел, чтобы мои студенты могли проверять свой код, когда они записывают его в записную книжку Jupyter, вызывая функцию из импортированного модуля, который запускает unittest. Это работает нормально, если только функцию не нужно проверять по объектам, которые должны быть выбраны в глобальной области Notebook.
Вот мой check_test
модуль:
import unittest
from IPython.display import Markdown, display
def printmd(string):
display(Markdown(string))
class Tests(unittest.TestCase):
def check_add_2(self, add_2):
val = 5
self.assertAlmostEqual(add_2(val), 7)
def check_add_n(self, add_n):
n = 6
val = 5
self.assertAlmostEqual(add_n(val), 11)
check = Tests()
def run_check(check_name, func, hint=False):
try:
getattr(check, check_name)(func)
except check.failureException as e:
printmd('**<span style="color: red;">FAILED</span>**')
if hint:
print('Hint:', e)
return
printmd('**<span style="color: green;">PASSED</span>**')
Если записная книжка:
In [1]: def add_2(val):
return val 2
In [2]: def add_n(val):
return val n
In [3]: import test_checks
In [4]: test_checks.run_check('check_add_2', add_2)
PASSED
In [5]: test_checks.run_check('check_add_n', add_n)
!!! ERROR !!!
Ошибка здесь не удивительна: add_n
не знает о n
I, определенном в check_add_n
.
Итак, я подумал, что мог бы сделать что-то вроде:
In [6]: def add_n(val, default_n=None):
if default_n:
n = default_n
return val n
в записной книжке, а затем прохождение n
теста:
def check_add_n(self, add_n):
val = 5
self.assertAlmostEqual(add_n(val, 6), 11)
Но это вызывает у меня UnboundLocalError
головную боль в дальнейшем из-за назначения n
, даже внутри if
предложения: это, по-видимому, мешает ноутбуку n
работать в глобальном масштабе, когда это необходимо.
Во избежание сомнений я не хочу настаивать на том, что n
передается в качестве аргумента add_n
: может быть много таких объектов, которые используются, но не изменяются тестируемой функцией, и я хочу, чтобы они разрешались во внешней области.
Есть идеи, как это сделать?
Комментарии:
1. Хотя я ответил на ваш вопрос, я нахожу эту
add_n
функцию довольно уродливой. Я бы предпочел, чтобы учащиеся написали функцию, подобнуюdef make_adder(n): return lambda val: val n
and thenadd_n = make_adder(n)
, и сохранили ееn
локальной…2. Спасибо за ваш ответ — это большая помощь. Однако я не уверен, что более продвинутый код, который вы предлагаете, будет легко понять новичкам.
Ответ №1:
Вы можете import __main__
получить доступ к области notebook:
import unittest
from IPython.display import Markdown, display
import __main__
def printmd(string):
display(Markdown(string))
class Tests(unittest.TestCase):
def check_add_2(self, add_2):
val = 5
self.assertAlmostEqual(add_2(val), 7)
def check_add_n(self, add_n):
__main__.n = 6
val = 5
self.assertAlmostEqual(add_n(val), 11)
check = Tests()
def run_check(check_name, func, hint=False):
try:
getattr(check, check_name)(func)
except check.failureException as e:
printmd('**<span style="color: red;">FAILED</span>**')
if hint:
print('Hint:', e)
return
printmd('**<span style="color: green;">PASSED</span>**')
Это дает мне PASSED
результат.
Это работает, потому что при выполнении файла python этот файл сохраняется в sys.modules
качестве __main__
модуля. Именно поэтому if __name__ == '__main__':
используется идиома. Можно импортировать такой модуль, и, поскольку он уже находится в кэше модуля, он не будет выполнять его повторно или что-либо еще.