Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

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?

like image 717
Diego Duarte Avatar asked Oct 31 '25 09:10

Diego Duarte


1 Answers

There are 2 problems.

Problem 1

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.

Problem 2

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:

  1. lambda_unit_test.py

    • By default, unittest looks for files matching the pattern test*.py
    • As to why, see the discover(start_dir, pattern='test*.py', top_level_dir=None) method of unittest's TestLoader class, which is "used to create test suites from classes and modules".
  2. lambda_returns_Rick_Sanchez

    • By default, unittest looks for methods that start with test (ex. test_function).
    • As to why, see the testMethodPrefix attribute of 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:

  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

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 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

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.

like image 96
Gino Mempin Avatar answered Nov 01 '25 23:11

Gino Mempin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!