Import path in pytest - python

I have Python Flask project with following structure of my project (I removed uneccesary things):
server
src
service
__init__.py
User.py
tests
pytest.ini
service
test_user.py
Where pytest.ini contains:
[pytest]
python_paths = ../src
And test_user.py:
from service.User import UserService
def test_empty_db():
service = UserService()
users = service.get_all() # Get all users from DB.
assert len(users) = 0 # There should be users.
Now, I would like to run this test. When I run pytest or pytest server from root of project, everything is ok. However, when I want to run the specified file pytest server/tests/service/test_user.py error appears:
from services.User import UserService
E ModuleNotFoundError: No module named 'service'
Is there any way to fix it?

You can try to make your imports more absolute inside your test_user.py like: from server.src.service.User import …
Or you can also try adding a __init__.py to your tests directory.

Your error message is showing services.User while you code is showing service.User, could it be a typo?

Related

Problem in using conftest.py from a packaged pytest framework

I am working on a pytest-framework that will be packed as a package. The setup file i am using for this is as this:
setup(
name='MyTestFrameWork',
version="2",
author='my name',
author_email='name#gmail.com',
description='My test framework',
long_description=open('README.md', 'rb').read().decode('utf-8'),
url='http://my.test.framework.dk',
license="Free loot",
packages=find_namespace_packages(),
python_requires=">=3.10",
include_package_data=True,
install_requires=['pytest'],
entry_points={"pytest11": ["MyTestFrameWork = MyTestFrameWork"]},
)
In this package (in the root of this folder MyTestFrameWork ) I have a conftest.py with some fixtures.
MY problem/Question:
When I import my framework from another python project eg: by importing the testframework. I cant get the framework to use the fixtures in the conftest ...... however,....
if i move the content from the conftest.py into __init__.py in my framework ie: in MyTestFrameWork folder the fixtures are working as expected.
why it is like this ....why cant i have my fixtures in the conftest.py instead of having them in the __init__.py am i missing something ?
for better view of my file-structure on the framework:
You are missing the conftest module in the entry_points definition. The syntax is following:
entry_points={"pytest11": ["name_of_plugin = myproject.pluginmodule"]}
This means that for Pytest to load the fixtures from conftest.py, the setup should be following:
setup(
...
entry_points={"pytest11": ["MyTestFrameWork = MyTestFrameWork.conftest"]},
)
However, conftest.py is typically meant for auto-discovery within tests so in a plugin I'd actually use a different name for the module containing the fixtures.

pytest return ModuleNotFoundError when module imported in test file import another module within the same directory of the imported module

I am sorry if the title takes some time to understand. So here is the folder structure:
falcon_tut/
falcon_tut/
app.py
images.py
__init__.py
tests/
test_app.py
__init__.py
And some codes
####################
# app.py
####################
from images import Resource
images = Resource()
api = application = falcon.API()
api.add_route('/images', images)
# ... few more codes
####################
# test_app.py
####################
import falcon
from falcon import testing
import ujson
import pytest
from falcon_tut.app import api
#pytest.fixture
def client():
return testing.TestClient(api)
def test_list_images(client):
doc = {
'images': [
{
'href': '/images/1eaf6ef1-7f2d-4ecc-a8d5-6e8adba7cc0e.png'
}
]
}
response = client.simulate_get('/images')
result_doc = ujson.loads(response.content)
assert result_doc == doc
assert response.status == falcon.HTTP_OK
It works fine when running with python falcon_tut/app.py and curl it with response of 200 and payload of the images
Until running pytest tests/ from the project root it output this:
ImportError while importing test module ../falcon_tut/tests/test_app.py
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/test_app.py:6: in <module>
from falcon_tut.app import api
E ModuleNotFoundError: No module named 'falcon_tut'
I tried creating __init__.py at the project root but it still output the same error above
Python version 3.7.0, with falcon 1.4.1, cpython 0.28.5, pytest 3.7.3, and instead of using gunicorn, I am using bjoern 2.2.2
I am trying out the python falcon framework and encounter the error at the testing part.
==========UPDATE===========
The reason why pytest could not found the module is because sys.path does not have ../falcon_tut/falcon_tut exists.
When I ran pytest and edit those 2 files and printing out sys.path, it only has [../falcon_tut/tests, ../falcon_tut, ..]. The workaround to this is to append the path to the package to sys.path. So here is the edited app.py
#############
# app.py
#############
import sys
# this line is just example, please rewrite this properly if you wants to use this workaround
# sys_path[1] only applied to my situation, again this is just example to know that it works
# the idea is to make sure the path to your module exists in sys.path
# in this case, I appended ../falcon_tut/falcon_tut to sys.path
# so that now ../falcon_tut/falcon_tut/images.py can be found by pytest
sys.path.insert(0, '{}/falcon_tut'.format(sys_path[1]))
# body codes...

pytest can't access django classes

I have a Django application, working fine. When I run the tests with pytest, it only works with utility classes (so not Django related).
For example, a test from package A calling an utility class from this package, or another, works fine.
However, I'm facing an error as soon as I import a django class.
example 1 :
I import my model (in the test), starting the test class with :
from app.common.models import Country
--> ImportError: No module named django.db
[django.db is called in models.py]
example 2 : I import an url resolver (in the test), starting the test class with :
from django.core.urlresolvers import reverse
--> ImportError: No module named django.core.urlresolvers
first try of fix
Following another topic, I set the content of PYTHONPATH :
/home/user/pyenv/lib/python3.5/site-packages
This folder contains installed packages in the virtualenv : django, pytest, psycopg2, and more.
1) If I set DJANGO_SETTINGS_MODULE to the same file as the application, "py.test" gives this error ending with :
File "/home/user/pyenv/lib/python3.5/site-packages/django/db/backends/postgresql/base.py", line 24, in
raise ImproperlyConfigured("Error loading psycopg2 module: %s" % e)
2) If I set DJANGO_SETTINGS_MODULE to a smaller test setting file, containing only the database infos, I face a different error (self.client failing in a test) :
class Test(unittest.TestCase):
def testUrls(self):
response = self.client.get('/countries/all/get')
self.assertEqual(response.status_code, 200)
--> AttributeError: 'Test' object has no attribute 'client'
more infos :
1) The python interpreter, for the application, is located in a virtualenv.
2) conftest.py is located in application root folder, so same place as manage.py, and has the following content:
import os
import sys
sys.path.append(os.path.dirname(__file__))
3) pytest.ini is located in same folder as manage.py too, with this content :
[pytest]
python_files = test_*.py test*.py
4) most important : the application works fine, so db settings are valid
If you have any idea of what's wrong, and how to test django classes, any idea will be welcomed. Thank you in advance.
I use pytest-django and I also explicitly set DJANGO_SETTINGS_MODULE=project.my_settings in pytest.ini. Works great every time.
I faced other issues trying the standard Django testing tool + coverage.
Using Pytest with "--cov" provided me what I needed, in one command only.
I finally found a fix for the issue. After trying again, and so restarting everything (pc, virtualenv), it worked fine.
So :
1) start virtualenv and move to the application folder
2) don't set PYTHONPATH
3) set DJANGO_SETTINGS_MODULE to application settings in pytest.ini
(so only one settings file in the project)
4) run the test with : py.test --cov=my_application_name
Now I can test models or forms without error.
For the previously mentioned test about urls, it works now with the following syntax:
import unittest
from django.test import Client
class Test(unittest.TestCase):
def setUp(self):
# global variable
self.client = Client()
def testUrls(self):
reponse = self.client.get('/countries/all/get')
self.assertEqual(reponse.status_code, 200)

More elegant solution for python imports across app/tests?

I'm trying to keep my code reasonably organized by putting tests in a separate directory from my app. However, imports work either for the app or for the tests, but not both. Here's a contrived example that illustrates my current problem:
myapp/
app/
main.py
settings.py
moods.py
test/
test_moods.py
Contents of files as follows:
main.py
import settings
from moods import Moods
words = Moods(settings.EXAMPLE)
print(words.excited())
settings.py
EXAMPLE = "Wow$ Python$"
DELIM = "$"
moods.py
import settings
class Moods:
def __init__(self, text):
self.text = text
def excited(self):
return self.text.replace(settings.DELIM, "!!!")
test_moods.py
import sys, os, unittest
sys.path.insert(0, os.path.abspath('..'))
from app.moods import Moods
class TestMood(unittest.TestCase):
def setUp(self):
self.words = Moods("Broken imports$ So sad$")
def test_mood(self):
self.assertEqual(self.words.excited(), "Broken imports!!! So sad!!!")
with self.assertRaises(AttributeError):
self.words.angry()
if __name__ == "__main__":
unittest.main()
In the current state, from myapp/ I can run the following successfully:
>> python3 app/main.py
Wow!!! Python!!!
But when I try to run tests, the import fails in moods.py:
>> python3 -m unittest discover test/
ImportError: Failed to import test module: test_moods
[ stack trace here pointing to first line of moods.py ]
ImportError: No module named 'settings'
If I modify line 1 of moods.py to read from app import settings, the test will pass, but normal execution of the app fails because it sees no module app.
The only solutions I can think of are
Putting the tests in the same directory as the code (which I was trying to avoid)
Adding the sys.path.insert(0, os.path.abspath('..')) to each file in my app to make the imports work the same as the test ones do (which seems messy)
Is there are more elegant way to solve this import problem?
You should have both app and test directory in PYTHONPATH.
One way is to replace this:
sys.path.insert(0, os.path.abspath('..'))
from app.moods import Moods
with this:
sys.path.insert(0, os.path.abspath('../app'))
from moods import Moods
Or you can set the PYTHONPATH environament variable before running the tests.
Why? Because when you run main.py, then app is in the path, not app/.., so you want to have the same when running tests.

Optimal file structure organization of Python module unittests?

Sadly I observed that there are are too many ways to keep your unittest in Python and they are not usually well documented.
I am looking for an "ultimate" structure, one would accomplish most of the below requirements:
be discoverable by test frameworks, including:
pytest
nosetests
tox
the tests should be outside the module files and in another directory than the module itself (maintenance), probably in a tests/ directory at package level.
it should be possible to just execute a test file (the test must be able to know where is the module that is supposed to test)
Please provide a sample test file that does a fake test, specify filename and directory.
Here's the approach I've been using:
Directory structure
# All __init__.py files are empty in this example.
app
package_a
__init__.py
module_a.py
package_b
__init__.py
module_b.py
test
__init__.py
test_app.py
__init__.py
main.py
main.py
# This is the application's front-end.
#
# The import will succeed if Python can find the `app` package, which
# will occur if the parent directory of app/ is in sys.path, either
# because the user is running the script from within that parect directory
# or because the user has included the parent directory in the PYTHONPATH
# environment variable.
from app.package_a.module_a import aaa
print aaa(123, 456)
module_a.py
# We can import a sibling module like this.
from app.package_b.module_b import bbb
def aaa(s, t):
return '{0} {1}'.format(s, bbb(t))
# We can also run module_a.py directly, using Python's -m option, which
# allows you to run a module like a script.
#
# python -m app.package_a.module_a
if __name__ == '__main__':
print aaa(111, 222)
print bbb(333)
module_b.py
def bbb(s):
return s + 1
test_app.py
import unittest
# From the point of view of testing code, our working modules
# are siblings. Imports work accordingly, as seen in module_a.
from app.package_a.module_a import aaa
from app.package_a.module_a import bbb
class TestApp(unittest.TestCase):
def test_aaa(self):
self.assertEqual(aaa(77, 88), '77 89')
def test_bbb(self):
self.assertEqual(bbb(99), 100)
# Simiarly, we can run our test modules directly as scripts using the -m option,
# or using nose.
#
# python -m app.test.test_app
# nosetests app/test/test_app.py
if __name__ == '__main__':
unittest.main()

Categories