如何使用 unittest 修复测试代码中被测模块的发现?

How to fix discovery of module-under-test in test code with unittest?

我一直在为这个错误而苦苦挣扎,我不知道为什么会这样。

ModuleNotFoundError: No module named 'lambdaFunction'

这是文件夹结构。

出于某种原因,当我尝试 运行 lambda_unit_test.py 时,它总是失败。

这是里面的代码:

import unittest
from lambdaFunction import index


class LambdaTest(unittest.TestCase):
    def lambda_returns_Rick_Sanchez(self):
        self.assertEquals(index.lambda_handlerx(), "Should return Rick Sanchez")


if __name__ == '__main__':
    unittest.main()

我也试过 import lambdaFunction 之类的东西,但没有成功。

这些是命令行输出:

python3 -m unittest test.lambda_unit_test.py
E
======================================================================
ERROR: lambda_unit_test (unittest.loader._FailedTest)
----------------------------------------------------------------------
ImportError: Failed to import test module: lambda_unit_test
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/unittest/loader.py", line 154, in loadTestsFromName
    module = __import__(module_name)
ModuleNotFoundError: No module named 'test.lambda_unit_test'


----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

如果我从根文件夹尝试 运行ning python3 -m unittest test.lambda_unit_test 它不会 运行 任何测试:

python3 -m unittest test.lambda_unit_test  

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

关于如何解决这个问题有什么想法吗?

import unittest

#the two lines below make anything located two levels above this file importable
import os.path, sys; 
sys.path.append(os.path.join(os.path.abspath(__file__),*['..']*2))

from lambdaFunction import index

有2个问题。

问题 1

第一个问题 是“ModuleNotFoundError:没有名为 'lambdaFunction' 的模块”,这通常是由您如何正在 运行宁 unittest。如 Running unittest with typical test directory structure (specifically, this nice answer 中所述),您必须确保 unittest 找到您的待测模块和测试文件。

所以,给定

myproject
├── lambdaFunction
│   ├── __init__.py
│   └── index.py
└── tests
    ├── __init__.py
    └── lambda_unit_test.py

你运行unittestmyproject文件夹下:

$ cd myproject
$ python3 -m unittest discover

这会告诉 unittest 使用其 Test Discovery 功能来查找您的测试文件和测试方法。这将 自动修复你的导入路径 (sys.path),这样你的测试就可以导入你的模块,而不需要你绕过 sys.path

如果发现工作正常,那么 from lambdaFunction import index 应该没问题,没有任何 ,这在重命名和移动文件和文件夹时很容易中断。

问题 2

第二个问题是命名,特别是测试文件的名称和测试方法的名称。它们不遵循测试发现中使用的 TestLoader.discover() 使用的默认模式:

  1. lambda_unit_test.py

  2. lambda_returns_Rick_Sanchez

    • 默认情况下,unittest 查找以 test 开头的方法(例如 test_function)。
    • 至于为什么,请参阅testMethodPrefix attribute of unittest's TestLoader class,这是一个“字符串,给出了将被解释为测试方法的方法名称的前缀。默认值为'test'."

如果您按原样 运行 python3 -m unittest discover,预计会得到“运行 0 次测试”。您可以 specify the exact test to run,跳过测试发现模式问题,通过传入 test_module.TestClass.test_method:

$ python3 -m unittest test.lambda_unit_test.LambdaTest.lambda_returns_Rick_Sanchez

但这不是一个好的解决方案。解决此问题的一种方法是简单地遵循 Test Discovery 使用的默认模式。像这样重命名您的测试文件和测试方法:

  1. lambda_unit_test.py --> test_lambda_functions.py
  2. lambda_returns_Rick_Sanchez --> test_lambda_returns_Rick_Sanchez
$ python3 -m unittest discover -v
test_lambda_returns_Rick_Sanchez (test.test_lambda_functions.LambdaTest) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

如果您真的想要使用自定义测试文件名and/or测试方法名,您将不得不自定义测试加载器。请参阅文档的 load_tests Protocol 部分:

Modules or packages can customize how tests are loaded from them during normal test runs or test discovery by implementing a function called load_tests.

有多种方法可以实现 load_tests,一种方法是在 test/__init__.py 文件中添加一个 load_tests 函数,如 load_tests Protocol 中所述:

If discovery is started in a directory containing a package, either from the command line or by calling TestLoader.discover(), then the package __init__.py will be checked for load_tests.

# In test/__init__.py

from pathlib import Path
from unittest import TestSuite

def load_tests(loader, tests, pattern):
    suite = TestSuite()
    loader.testMethodPrefix = 'lambda_returns'
    tests = loader.discover(
        Path(__file__).parent.as_posix(),
        pattern='*_test.py',
    )
    suite.addTests(tests)
    return suite

该代码将测试的方法名称前缀更改为 lambda_returns 并查找具有模式 *_test.py 的文件,两者都覆盖了默认行为。

有了它,它最终应该找到您的测试:

$ python3 -m unittest discover -v
lambda_returns_Rick_Sanchez (test.lambda_unit_test.LambdaTest) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

如果可以,我强烈建议您遵循测试文件和测试方法名称的默认模式。它越清晰(至少对我而言)并且需要维护的代码越少越好。

作为旁注,self.assertEquals is now deprecated。您应该改用 assertEqual.