I. Preface: The application directory structure and modules are listed at the end of the post.
II. Statement of the Problem: If the PYTHONPATH is not set the application runs, but the unittest fails with the ImportError: No module named models.transactions. This happens when trying to import
Transactions in app.py. If the PYTHONPATH is set to /sandbox/app both the application and
unittest run with no errors. The constraints for a solution are that the PYTHONPATH should not have to be set, and
the sys.path should not have to be modified programmatically.
III. Details: Consider the case when the PYTHONPATH is set and the test_app.py is run as a package /sandbox$ python -m unittest tests.test_app. Looking at the print statements for __main__
sprinkled throughout the code:
models : app.models.transactions
models : models.transactions
resources: app.resources.transactions
app : app.app
test : tests.test_app
The unittest imports the app first, and so there is app.models.transactions. The next import that the app
attempts is resources.transactions. When it is imported it makes its own import of models.transactions, and
then we see the __name__ for app.resources.transactions. This is followed by the app.app
import, and then finally the unittest module tests.test.app. Setting the PYTHONPATH allows the application to resolve models.transactions!
One solution is to put the models.transactions inside resources.transaction. But is there another way to handle the problem?
For completeness, when the application is run the print statements for __main__ are:
models : models.transactions
resources: resources.transactions
app : __main__
This is expected, and no imports are being attempted which are above /sandbox/app or sideways.
IV. Appendix
A.1 Directory Structure:
|-- sandbox
|-- app
|-- models
|-- __init__.py
|-- transactions.py
|-- resources
|-- __init__.py
|-- transactions.py
|-- __init__.py
|-- app.py
|-- tests
|-- __init__.py
|-- test_app.py
A.2 Modules:
(1) app:
from flask import Flask
from models.transactions import TransactionsModel
from resources.transactions import Transactions
print ' app : ', __name__
def create_app():
app = Flask(__name__)
return app
app = create_app()
if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000, debug=True)
(2) models.transactions
print ' model : ', __name__
class TransactionsModel:
pass
(3) resources.transactions:
from models.transactions import TransactionsModel
print ' resources: ', __name__
class Transactions:
pass
(4) tests.test_app
import unittest
from app.app import create_app
from app.resources.transactions import Transactions
print ' test : ', __name__
class DonationTestCase(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_transactions_get_with_none_ids(self):
self.assertEqual(0, 0)
if __name__ == '__main__':
unittest.main()