We have a bunch of commands in our Django site, some that are administrative and some that run on cron jobs that I can't figure out how to test. They pretty much look like this:
# Saved in file /app/management/commands/some_command.py
# Usage: python manage.py some_command
from django.core.management.base import NoArgsCommand
class Command(NoArgsCommand):
def handle_noargs(self, **options):
# Do something useful
And I have some tests, which look like this:
import unittest
from django.test import TestCase
from django_webtest import WebTest
class SomeTest(WebTest):
fixtures = ['testdata.json']
def setUp(self):
self.open_in_browser = False
# Set up some objects
def test_registration(self):
response = self.client.get('/register/')
self.assertEqual(response.status_code, 200)
form = self.app.get('/register/').forms[1]
# Set up the form
response = form.submit()
self.assertContains(response, 'You are Registered.')
if self.open_in_browser:
response.showbrowser()
# Here I'd like to run some_command to see the how it affects my new user.
In my test (where I have the comment) I'd like to run my NoArgsCommand to see what happens to my new user. I can't find any documentation or examples on how to accomplish this. Also note that my test environment is a SQLlite DB that I create from scratch in memory, load some fixtures and objects into and run my tests, so as much as I'd like to setup the data in a real DB, then just run my command from the command line, I can't, it's far too time consuming. Any ideas would be greatly appreciated.
Django documentation on management commands might help, it describes how to call them from python code.
Basically you need something like this:
from django.core import management
management.call_command( ... )
Related
I am following the testdriven.io Test-Driven Development with FastAPI and Docker tutorial and I am stuck on the Pytest setup step. I have checked over an over again to see what I am missing, and keep coming up short.
The code sample from the tutorial shows that, in conftest.py, you are to ahve the following from statement:
from app import main
from app.config import get_settings, Settings
For starters, Pycharm is telling me that it is unable to import anything from above.
My Folder Structure:
main.py:
import os
from fastapi import FastAPI, Depends
from tortoise.contrib.fastapi import register_tortoise
from .config import get_settings, Settings
app = FastAPI()
register_tortoise(
app,
db_url=os.environ.get("DATABASE_URL"),
modules={"models": ["app.models.tortoise"]},
generate_schemas=False,
add_exception_handlers=True,
)
#app.get("/ping")
async def pong(settings: Settings = Depends(get_settings)):
return {"ping": "pong", "environment": settings.environment, "testing": settings.testing}
conftest.py
import os
import pytest
from starlette.testclient import TestClient
from app import main
from app.config import get_settings, Settings
def get_settings_override():
return Settings(testing=1, database_url=os.environ.get("DATABASE_TEST_URL"))
#pytest.fixture(scope="module")
def test_app():
# set up
main.app.dependency_overrides[get_settings] = get_settings_override
with TestClient(main.app) as test_client:
# testing
yield test_client
# tear down
The tutorial has you run the tests using docker-compose exec web python -m pytest
This is the output I get when running the tests:
Any help would be appreciated. I feel like this is entry level stuff that is causing an extreme headache.
Thanks to #MatsLindh for the help. As he mentioned in his comments above, the tutorial has you running pytest on the entire project instead of just the tests folder. Running directly on tests solved my issue with pytest failing. He also gave good advice on getting imports to work correctly in an IDE by suggesting to look at the pytest documentation for further integration steps.
To run my Django's tests, I was using the manage.py file.
But I decided to create my own file runtests.py.
As specified in the Django's doc, the manage.py file will execute all methods whose name is starting like "test" and whose class inherits TestCase !
My aim is to use this searching method to display all possible tests (not running them).
So do you know where I could get this "searching method" ?
Thank you !
What you're looking for is called "test discovery", and you don't need to do it manually.
If you want to write a standalone script that runs your tests, you can simply invoke Django's test runner manually from Python code after configuring. The process for this is:
# First you need settings.
# You can do this by setting DJANGO_SETTINGS_MODULE:
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'path.to.your.settings')
# Or you can do it manually:
SETTINGS_DICT = {
# Put all the settings you'll need in here...
}
from django.conf import settings
settings.configure(**SETTINGS_DICT)
import django
django.setup()
import sys
# Now you can run tests:
from django.test.utils import get_runner
TestRunner = get_runner(settings)
test_runner = TestRunner(verbosity=2, interactive=True)
failures = test_runner.run_tests(
['test_module_to_look_in']
)
sys.exit(bool(failures))
I'm going through the Django tutorial and wanted to try out database manipulating with a python script but encounter a problem
My script:
from polls.models import Question, Choice
from django.utils import timezone
import numpy as np
#Questions
nquestions=3
q=np.empty([nquestions, 10], dtype=object)
qtext=q
qtext[0]="What's up?"
qtext[1]="What's new?"
qtext[2]="What's old?"
#Choices
q[0,1]="Not much"
q[0,2]="The sky"
q[1,1]="This question"
q[1,2]="This answer"
q[2,1]="No more originality"
q[2,2]="xxxxxxx"
#Check if exists and apply
for i in range(0, len(q)):
q[i,0]=Question(question_text=qtext[i], pub_date=timezone.now())
if Question.objects.filter(question_text=qtext[i]).exists():
pass
else:
q[i,0].question_text=qtext[i]
q[i,0].save()
alen=len(q[i][q[i] != np.array(None)])
for ii in range(0, alen-1):
q[i,0].choice_set.create(choice_text=q[i,ii+1], votes=0)
I get the error django.core.exceptions.appregistrynotready apps aren't loaded yet. I'm running the script from terminal and it's places in the folder that contains the polls-folder (One level over modules.py, which I'm trying to import).
Writing the contents of my script works if i first run in terminal : python manage.py shell. Is there a way for me to run a script to manipulate the database through Djangos API, or do I have to enter information manually or send requests thŕough post?
You need to set the DJANGO_SETTINGS_MODULE environment variable and call django.setup() before you can import your models.
import os
import django
# Change mysite if your project has a different name
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
django.setup()
from polls.models import Question, Choice
...
See the docs for more info.
Another option is to write a custom management command. When you use manage.py to run your management command you don't have to set DJANGO_SETTINGS_MODULE or call django.setup().
I try to work with a class from Django views.
sync.py
import requests
class Sync:
def do():
r = requests.post('http://192.168.1.7/api/report_x')
print(r.response)
views.py
import sync
def index(request):
s = Sync()
s.do()
return HttpResponse("Hello, this is index")
When I access index from the browser I get:
global name 'Sync' is not defined
What am I doing wrong?
If you're writing Python, you should actually write Python. Python is not Java.
You should not be using a class here at all. Define a standalone function called sync, import it with from sync import sync, and just call it. No need for an instance of something that has no state.
Direct reference to Sync() will not work with current import statement.
Change it to either:
from sync import Sync
Or use:
s = sync.Sync()
Import the name before using it
from sync import Sync
Assuming that your file directory is proper and not the reason for the error, change your code to the following:
from sync import Sync # simply import sync just calls the synv.py file, not the Sync class
def index(request):
s = Sync()
s.do()
return HttpResponse("Hello, this is index")
I have a command that I would like to test. It hits external services and I would like to mock out the function calls that hit these external services, only check that they were called with the proper arguments. The code looks like this:
import mock
from django.core.management import call_command
from myapp.models import User
class TestCommands(TestCase):
def test_mytest(self):
import package
users = User.objects.filter(can_user_service=True)
with mock.patch.object(package, 'module'):
call_command('djangocommand', my_option=True)
package.module.assert_called_once_with(users)
When I run it however I keep getting AssertionError: Expected to be called once. Called 0 times. I assume this is because I am not actually calling the module within the context, I am calling it in call_command('djangocommand', my_option=True), but shouldn't all calls to this module be mocked out while the context is active? If not, does anyone have suggestions for how such a test could be conducted?
The reference you need to patch is the 'module' attribute reference in django.core.management. Attempting to mock the package reference in the test file doesn't change the reference in django.core.management.
You'll need to do something like
import mock
from django.core.management import call_command
import django.core.management
from myapp.models import User
class TestCommands(TestCase):
def test_mytest(self):
users = User.objects.filter(can_user_service=True)
with mock.patch.object(django.core.management, 'module'):
call_command('djangocommand', my_option=True)
django.core.management.module.assert_called_once_with(users)