boxed/hammett

Support for tests dir in subdirectory

Opened this issue · 2 comments

Looking at the code, hammett expects tests to be located in a "test" or "tests" directory located at the project root or located within the module directory (I'm guessing alongside the actual files that they test).

My project is set up like this:

root/
├─ src/
│  ├─ my_module/
│  │  ├─ __init__.py
│  │  ├─ my_app.py
│  ├─ tests/
│  │  ├─ __init__.py
│  │  ├─ test_my_app.py
setup.cfg
requirements.txt

Pytest is able to run fine when I set "testpaths=lib/tests", but I have not been able to configure hammett to run tests properly.

I tried this configuration which actually picked up the test classes, but then hammett couldn't figure out where my module was:

[hammett]
modules=src
source_location=.

It resulted in ModuleNotFoundError: No module named 'my_module'

It feels like I should be able to do this and it should work, but it doesn't find my tests:

[hammett]
modules=my_module
source_location=src

That just gives me the No module to test found. You might need to add... message.

Looking at the code, this might be fixed by simply adding a couple more lines to the collect_files method to also check for "test" or "tests" directories under the source_location path at this location:

hammett/hammett/__init__.py

Lines 315 to 321 in 0cd47f8

def collect_files(filenames):
result = []
if filenames is None:
handle_dir(result, f'tests{os.sep}')
handle_dir(result, f'test{os.sep}')
for module_name in g.modules:
handle_dir(result, join(g.source_location, module_name))

Messing around with it locally, it looks like there is still a problem with loading my_module from my test classes with hammett even if I get the test classes to get picked up.

When hammett tries to load the test module on the following line, it fails with ModuleNotFoundError: No module named 'my_module':

hammett/hammett/__init__.py

Lines 598 to 610 in 0cd47f8

def load_module(module_name, test_filename):
import importlib.util
spec = importlib.util.spec_from_file_location(module_name, test_filename)
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
try:
spec.loader.exec_module(module)
except Exception:
print(f'Failed to load module {module_name}:')
import traceback
print(traceback.format_exc())
g.results['abort'] += 1
return module

The module_name value passed-in here looks kind of weird: src.tests.test_my_app. I don't think the src part should be included, but if I remove it (using the debugger), I still get the same error as before.

boxed commented

Hmm.. Yea I guess there are some issues with that type of folder structure. I personally never have a src folder. In my opinion it just causes trouble as you have to fiddle with the python path and stuff to make it work.

I am open to a PR if you figure this out.