使用单元测试在 Jupyter 中测试学生的代码

xnx*_*xnx 5 python unit-testing python-3.x jupyter-notebook

我希望我的学生能够通过从运行单元测试的导入模块调用函数来检查他们在 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>**')
Run Code Online (Sandbox Code Playgroud)

如果笔记本是:

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 !!!
Run Code Online (Sandbox Code Playgroud)

这里的错误并不奇怪:add_n不知道n我在check_add_n.

所以我开始想我可以做这样的事情:

In [6]: def add_n(val, default_n=None):
            if default_n:
                n = default_n
            return val + n
Run Code Online (Sandbox Code Playgroud)

在 Notebook 中,然后通过n测试:

    def check_add_n(self, add_n):
        val = 5
        self.assertAlmostEqual(add_n(val, 6), 11)
Run Code Online (Sandbox Code Playgroud)

但这让我UnboundLocalError头疼不已,因为n即使在一个if子句中的赋值也是如此:这显然阻止了 Notebookn在需要时在全局范围内获取。

为免生疑问,我不想坚持将n其作为参数传递给add_n:可能有许多这样的对象使用但未被测试的函数更改,我希望它们在外部范围内解析。

任何想法如何解决这个问题?

Bak*_*riu 4

您可以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>**')
Run Code Online (Sandbox Code Playgroud)

这给了我一个PASSED输出。


这是有效的,因为当您执行 python 文件时,该文件将sys.modules作为__main__模块存储在其中。这正是if __name__ == '__main__':使用这个习语的原因。可以导入此类模块,并且由于它已经在模块缓存中,因此不会重新执行它或任何内容。