0

I have the following package layout (as recommended here):

 module/
   lib/
     __init__.py
     module.py
     util.py
   tests/
     env.py
     test_util.py

The file util.py contains a utility function f() which is re-used multiple times within the package. It's not exposed to users, since it doesn't make sense outside the context of the package, but it does make sense to write a unit test for it.

The file env.py contains these lines:

import os
import sys

sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

How do I set up my package to that the unit tests have access to util.f?

Update: After some back-and-forth in the comments and some testing it seems that adding the lib directory to the system path as well by adding:

    sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib')))

to env.py does the trick. However, this seems to break relative imports. I can live without relative imports for now, but is this a standard/robust way of setting up unit tests, or is this going to break in the long term?

Peter
  • 213
  • 1
  • 3
  • 8
  • 2
    "but it doesn't give me access to the internals" what does that mean? If you can import it, you can see everything in it that isn't deliberately hidden. – Jared Smith Jan 07 '19 at 16:16
  • I have the same question as the comment above. To add to this, have you taken a look to ensure that you are testing/mocking in the right place? That might be attributing to the difficulties you are having? – idjaw Jan 07 '19 at 16:21
  • @JaredSmith Perhaps I misunderstood how packaging works. Say util.py has a function f(), and ```__init__.py``` is empty. Are you saying I can access f() if I import the package? – Peter Jan 07 '19 at 16:22
  • @Peter yes. Yes you can: `from util import f` or `import util` and later `util.f()`. Basically, unless it's hidden in a closure or utilizes some other gimmickry, everything in a python file is visible to another file that imports it. Things you don't want to be part of the public API should be prefixed with an underscore. – Jared Smith Jan 07 '19 at 16:25
  • @JaredSmith But ```util``` is a module inside the package (called lib). I can do ```import lib```, but I can't access ```util.py``` or anything inside it from code that is outside the package ```lib```. – Peter Jan 07 '19 at 16:28
  • 1
    @Peter you already posted links and said things that suggested you modified your sys.path in env.py to allow importing for unit tests. What is your actual problem here? – Jared Smith Jan 07 '19 at 16:29
  • @JaredSmith I see what you mean. I thought ```env.py``` was just importing the package, but I see now that it's adding all the modules to the system path. I guess it should work but it doesn't. I'll look for the problem elsewhere. – Peter Jan 07 '19 at 16:33
  • Please do not provide additional information in comments. **EDIT** your question instead. Dont expect readers to go through a whole bunch of hard to read comments to get to your question! – GhostCat Jan 07 '19 at 16:39
  • 1
    @GhostCat I'll do that when it's clear what the problem is. Right now I don't even know whether the question makes sense. – Peter Jan 07 '19 at 16:41
  • Makes sense, to a certain degree. On the other hand, this place is a Q/A forum. You come here with questions, and get answers. The idea is not that you come here with a vague idea, and that people then extract the "real" question from that cloud of thoughts ... – GhostCat Jan 07 '19 at 16:43
  • I've updated the question. @JaredSmith's comment pointed me in the direction of a working solution, but I'm still curious whether this is the normal way of doing things. (It seems like this is an incredibly common use case, since this is the first unit test I tried to write) – Peter Jan 07 '19 at 17:57
  • 1
    @Peter I would avoid relative imports. I don't know how common it is, but this is basically the approach I use. I typically have my tests organized in my tests folder by type (unit, integration, end-to-end) and in each of those directories is a file called context.py that appends to sys.path and imports everything. Then the tests import from context. I point pytest at the appropriate directory, and watch the dots tick. – Jared Smith Jan 07 '19 at 18:24

0 Answers0