I've been struggling with this error and I have no idea of why it happens.
ModuleNotFoundError: No module named 'lambdaFunction'
This is the folder structure.
And for some reason when I try to run the lambda_unit_test.py it keeps failing.
This is the code in there:
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()
I've also tried things like import lambdaFunction
without any luck.
These are the command line outputs:
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)
If I try running python3 -m unittest test.lambda_unit_test
from the root folder it does not run any test:
python3 -m unittest test.lambda_unit_test
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
Any ideas on how to fix this?
There are 2 problems.
The 1st problem is the "ModuleNotFoundError: No module named 'lambdaFunction'", which is usually caused by how you are running unittest
. As described in Running unittest with typical test directory structure (specifically, this nice answer), you have to make sure unittest
finds both your modules-under-test and the test files.
So, given
myproject
├── lambdaFunction
│ ├── __init__.py
│ └── index.py
└── tests
├── __init__.py
└── lambda_unit_test.py
You run unittest
under the myproject
folder:
$ cd myproject
$ python3 -m unittest discover
That would tell unittest
to use its Test Discovery feature to find your test files and test methods. That would also automatically fix your import paths (sys.path
), so that your tests would be able to import your modules, without you needing to hack around sys.path
.
If the discovery works correctly, then from lambdaFunction import index
should be fine, without any modifications to sys.path
, which can easily break when renaming and moving around files and folders.
The 2nd problem is naming, specifically the names of your test files and names of your test methods. They don't follow the default patterns used by TestLoader.discover()
which is used in Test Discovery:
lambda_unit_test.py
unittest
looks for files matching the pattern test*.py
unittest
's TestLoader class, which is "used to create test suites from classes and modules".lambda_returns_Rick_Sanchez
unittest
looks for methods that start with test
(ex. test_function
).unittest
's TestLoader class, which is a "string giving the prefix of method names which will be interpreted as test methods. The default value is 'test'."If you just run python3 -m unittest discover
as is, expect to get "Ran 0 tests". You can specify the exact test to run, skipping the Test Discovery pattern problems, by passing in test_module.TestClass.test_method
:
$ python3 -m unittest test.lambda_unit_test.LambdaTest.lambda_returns_Rick_Sanchez
But that's not a good solution. One option to fix this is to simply follow the default patterns used by Test Discovery. Rename your test files and test methods like so:
lambda_unit_test.py
--> test_lambda_functions.py
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
If you really want to use custom test file names and/or test method names, you will have to customize the test loader. See the load_tests Protocol section of the docs:
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
.
There are a number of ways to implement load_tests
, one way is to add a load_tests
function in your test/__init__.py
file, as instructed in the 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 forload_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
That code changes the method name prefix for tests to lambda_returns
and to look for files with pattern *_test.py
, both overriding the default behavior.
With that it should finally find your tests:
$ python3 -m unittest discover -v
lambda_returns_Rick_Sanchez (test.lambda_unit_test.LambdaTest) ... ok
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
If you can, I highly recommend following instead the default patterns for test files and test method names. It's clearer (to me at least) and less code to maintain, the better.
As a side note, self.assertEquals
is now deprecated. You should use instead assertEqual
.