7

I would like to write unit tests to test whether a dependency exists between two python packages. E.g.:

a/
    __init__.py
    models.py
    views.py
    ...
b/
    __init__.py
    models.py
    views.py
    ...

a unit test to check that modules in package b don't import anything from modules in package a. The only solution I have so far is to scan the files and check that there isn't a "from a" or "import a" in the source code. Are there other ways of doing this? One of the requirements is that a/ and b/ must be in the same directory level.

I would like to have this unit test because I want to be sure that I can use package b in other projects without package a, and also not have other developers write code that will make b dependent on a.

Derek Kwok
  • 12,768
  • 6
  • 38
  • 58

3 Answers3

5

Python is too dynamic to do this 100% correctly. Consider that you can import modules by calling __import__, which takes a string argument so the name of the module to import can be constructed at runtime. Also, __import__ is a function, so it could be bound to other names, so you can't even be sure of detecting all the cases when something is being imported.

And it's technically possible for a module to call a function from another module, which imports a module and returns it. So you definitely can't do this by analysing just package b.

And then there's exec to execute arbitrary python code constructed at runtime...

The closest you could get is probably to try and make your unit test exercise b when a is on the PYTHONPATH, and also when a isn't on the PYTHONPATH. Still not foolproof, as that only tells you that b completed all the tests without a on the PYTHONPATH, not that it doesn't ever need a for anything. And if you're really unlucky, b does something really stupid and fiddles with sys.path in flight and manages to import a anyway somehow.

If, however, this is all your your own code and you know you don't do this kind of wacky crap, then a simple script that scans the files for import statements is probably your best bet. It would probably work very very often on random other people's code too. It's just not possible to do the job perfectly with full generality.

Ben
  • 68,572
  • 20
  • 126
  • 174
1
import sys
sys.modules['a'] = None

import b
# run unit tests for b to try and catch local import statements in b's functions
Igor Nazarenko
  • 2,184
  • 13
  • 10
0

There are modules like coverage.py that are able to check which code was executed during a test. Therefore there must be a way to check which module that code is part of which was executed during a test. This approach should work, regardless of Python's dynamic nature, contrary to what @Ben is saying in his answer.

However I'm not aware of a module that does this for you out of the box. So this sounds like a lot of manual work, but also a good possibility to release a new testing utility for this.

Gregor Müllegger
  • 4,973
  • 1
  • 23
  • 22
  • This is just the unit test vs static analysis dichotomy. It **is** possible to *check* whether code from module A was executed in a particular execution run of something from module B. It is **not** possible to analyse module B and *guarantee* that it never imports module A. Testing is often enough, but it won't completely fill the OP's need because it depends on the tests actually exercising all of the relevant code. If other developers might introduce the dependency with new additions to the module, there's no guarantee they'll also add test cases that will catch this. – Ben May 03 '22 at 23:44