pytest can't access django classes - python

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)

Related

Python script not run in Django environment

Terminal says that i didn't defined DJANGO_SETTINGS_MODULE but i tried as i thought every method to do it and so far nothing helper (coding on windows)
Can someone help me out please? I am stuck and dont know what to do
Maby this will help - i run my virtual env through Anaconda -
conda activate djangoenv
raise ImproperlyConfigured(django.core.exceptions.ImproperlyConfigured: Requested setting INSTALLED_APPS, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.
from faker import Faker
from app.models import AccessRecrod, Webpage, Topic
import random
import django
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
django.setup()
fakegen = Faker()
topics = ['Search', 'Social', 'Marketplace', 'News', 'Games']
def add_topic():
t = Topic.objects.get_or_create(top_name=random.choice(topics))[0]
t.save()
return t
def populate(N=5):
for entry in range(N):
# get topic for entry
top = add_topic()
# create fake data for entry
fake_url = fakegen.url()
fake_date = fakegen.date()
fake_name = fakegen.company()
# create new webpage entry
webpg = Webpage.objects.get_or_create(
topic=top, url=fake_url, name=fake_name)[0]
# create fake access record
acc_rec = AccessRecrod.objects.get_or_create(
name=webpg, date=fake_date)[0]
if __name__ == '__main__':
print('populating script!')
populate(20)
print('populating complete!')
The Problem is simply your import of models. You just need to setup django before you import anything related to django:
import django
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
django.setup()
from faker import Faker
from app.models import AccessRecrod, Webpage, Topic
import random
rest of your code
If you check the code in manage.py and django code on github you will essentially find the same sequence of code (besides a lot of additional stuff) when you run a management command by "python manage.py your_command"
To use the management command directly as described in the answer from #Code-Apprentice would be the other "normal" or "more django way" to integrate your script.
But you asked for the reason of the error message in your way of doing it.
If you study the complete error trace you will also find that it starts at the import command.
Instead of trying to shoe horn a regular python script into the Django environment like this, I suggest you create a command which you can run with ./manage.py. You can learn how to create your own custom commands here. When you do this correctly, you can run a command like ./manage.py create_topics or whatever name you give your command. This allows manage.py to load the Django environment for you.
Alternatively, you could look at creating fixtures with ./manage.py dumpdata and ./manage.py loaddata. These commands will allow you to create data and save them to a .json file which you can load into your database at any time.

Import path in pytest

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?

import of Python class from module in package

My structure looks something like this:
project/->
app.py
checker/->
exc.py
anc.py
My files are simple:
# app.py
from checker.exc import ExampleClass
# checker/exc.py:
from anc import AnotherClass
class ExampleClass(AnotherClass):
print('Example')
# checker/anc.py:
class AnotherClass:
print('AAAA')
When I run exc.py inside checker folder everything works ok, same when
I run app.py with module from package checker everything works perfect.
But when I run app.py which uses class from checker.exc, and exc need anc. I have an error ModuleNotFoundError: No module named anc
Realise this is a duct tape solution.
Change exc.py to:
try:
from anc import AnotherClass
print('abs import')
except ModuleNotFoundError:
from .anc import AnotherClass
print('rel import')
class ExampleClass(AnotherClass):
print('Example')
That way you can use absolute import when debugging, for instance, but rely on relative import when importing it running app.py on it's own.
The order in which you attempt to import them should reflect the expected use, with the one most expected to be used being attempted first. The error is the same if you switch the attempts.
Since the code is being run from the project folder, in order for exc.py to find anc.py you need to change exc.py to the following:
from .anc import AnotherClass
class ExampleClass(AnotherClass):
print('Example')
As berna1111's comment suggests, this may cause problems when running exc.py directly.

How to use functions from a custom module in Python Django shell

I have a python file called my_functions.py in which I have the following code:
from core.models import Blog
def news():
b = Blog(name='New Blog', tagline='All the latest news.')
b.save()
My main app folder in django is called core and I have put my python file in there. In the shell I am able to do the import: from core import my_functions
However I get an error AttributeError: module 'core.my_functions' has no attribute 'news' when I try to run the code my_functions.news().
How can I run the news function in the shell?
My tree structure is as follows:
core
-__init__.py
-admin.py
-apps.py
-models.py
-my_functions.py
-tests.py
-urls.py
-views.py
Everthing else works as normal but I just cant seem to figure why I cant do this simple import and run the function. I'm using VSCode.
Make sure there's an __init__.py file in the core directory. Then:
from core.my_functions import news
Also you have to restart your shell if you make changes to any file in your project, since the django shell will load all modules in memory at launch time.
Create your file and write your function. In my case was:
# /home/$(USER)/repos/bo/apps/base/queries.py
# "apps.base", added to INSTALLED_APPS list in settings.py
from apps.base.models import Player
from core.models import Blog
def news():
b = Blog(name='New Blog', tagline='All the latest news.')
b.save()
return b
def count_player_overs():
all_playerovers = Player.objects.all()
count_all_playerovers = all_playerovers.count()
return count_all_playerovers
print('count_all_playerovers: {}'.format(count_player_overs()))
print('saved news object: {}'.format(news()))
# this folder has manage.py
cd /home/$(USER)/repos/bo/
And run:
./manage.py shell -c "from apps.base import queries;"
Result:
count_all_playerovers: 376
saved news object: New Blog
You can change the code and run again ./manage.py shell -c "from apps.base import queries;" and it will re-import the module and run the function with all your changes.

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...

Categories