【问题标题】:Testing students' code in Jupyter with a unittest使用单元测试在 Jupyter 中测试学生的代码
【发布时间】:2016-10-05 17:36:56
【问题描述】:

我希望我的学生能够通过调用运行单元测试的导入模块中的函数来检查他们在 Jupyter Notebook 中编写的代码。除非需要针对要在 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 不知道我在check_add_n 中定义的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:可能有很多这样的对象被使用但未被测试的函数改变,我希望它们在外部范围。

有什么想法可以解决这个问题吗?

【问题讨论】:

  • 当我回答你的问题时,我发现add_n 函数非常丑陋。我宁愿让学生写一个像 def make_adder(n): return lambda val: val +n 这样的函数,然后是 add_n = make_adder(n) 并保持 n 本地...
  • 感谢您的回答——这是一个很大的帮助。但是,我不确定您建议的更高级的代码对于初学者来说是否容易理解。

标签: python unit-testing python-3.x jupyter-notebook


【解决方案1】:

您可以import __main__ 访问笔记本范围:

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 文件时,该文件作为 __main__ 模块存储在 sys.modules 中。这正是使用if __name__ == '__main__': 成语的原因。可以导入这样的模块,因为它已经在模块缓存中,所以不会重新执行它或任何东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-04-16
    • 2011-01-15
    • 2015-08-19
    • 1970-01-01
    • 1970-01-01
    • 2011-05-15
    • 1970-01-01
    • 2015-03-06
    相关资源
    最近更新 更多