Testing Django Commands with Mock - python

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)

Related

How to mock classes or objects in a module under test with flexmock

I want to mock a function call in the body of the surrounding function which I'm testing. I need to do this with flexmock or at least know if it's not possible. Take the following piece of code as an example. I have 3 files. utils.py with a single function run_command and an api.py with a class that has a single method, the test_api.py. All of this is for demonstration purposes and isn't particularly useful but should be able to describe my problem
# util.py
import subprocess
def run_command(cmd):
return subprocess.check_output(cmd)
Now api.py will maake use of run_command from util.py
# api.py
from util import run_comand
class Api:
def get_update(self):
result = run_command(["pwd"]).decode("utf-8")
return f"Update: {result}"
Now I want to write a test for Api.get_update but I don't want to actually execute run_command
using the mock module, I would have written the test like
# test_api.py
from unittest import mock
#mock.patch("api.run_command")
def test_get_update(run_command_mock):
run_command_mock.return_value = b"sucess!!!"
api = Api()
result = api.get_update()
assert result == "Update: success!!!"
The above test works as expected. It doesn't actually execute run_command when get_update is called. It uses the mocked version. Now I want to do this with flexmock. I don't know if this is possible though. Just that I'm contributing to a project and all tests are built with flexmock so I want to follow the same pattern. Unfortunately,I couldn't find a test that solves a similar problem. From this SO mocking-functions-with-flexmock-in-python, I came up with this
import utils as utils_module
import flexmock
from api import Api
def test_get_update():
flexmock(utils_module).should_receive("run_command").with_args(["pwd"]).once().and_return("success!!")
api = Api()
result = api.get_update()
assert result == "Update: success!!!"
The above test fails. run_command from utils is still executed not my mocked version. How do I fix this using flexmock?
I figured out what I was doing wrong. I was mocking run_command from where it's coming from instead of where it's been used like I did with #mock.patch. To fix it, I had to rewrite my test like this
import flexmock import flexmock
import api as api_module # Instead of importing utils, I'm importing api since that's where run_command is called even though it's coming from utils
def test_get_update():
flexmock(api_module).should_receive("run_command").once().and_return(b"success!!!")
api = api_module.Api()
result = api.get_update()
assert result == "Update: success!!!"

Python/Django - No module named

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")

Import classes from child directory python

I've been trying to import some python classes which are defined in a child directory. The directory structure is as follows:
workspace/
__init__.py
main.py
checker/
__init__.py
baseChecker.py
gChecker.py
The baseChecker.py looks similar to:
import urllib
class BaseChecker(object):
# SOME METHODS HERE
The gChecker.py file:
import baseChecker # should import baseChecker.py
class GChecker(BaseChecker): # gives a TypeError: Error when calling the metaclass bases
# SOME METHODS WHICH USE URLLIB
And finally the main.py file:
import ?????
gChecker = GChecker()
gChecker.someStuff() # which uses urllib
My intention is to be able to run main.py file and call instantiate the classes under the checker/ directory. But I would like to avoid importing urllib from each file (if it is possible).
Note that both the __init__.py are empty files.
I have already tried calling from checker.gChecker import GChecker in main.py but a ImportError: No module named checker.gChecker shows.
In the posted code, in gChecker.py, you need to do
from baseChecker import BaseChecker
instead of import baseChecker
Otherwise you get
NameError: name 'BaseChecker' is not defined
Also with the mentioned folders structure you don't need checker module to be in the PYTHONPATH in order to be visible by main.py
Then in main.y you can do:
from checker import gChecker.GChecker

django testrunner not running my tests

I've got the following tests.py file:
from django.test import TestCase
from lxml import etree
import tempfile
import utils
class CreateSurveyFromCsvTextTests(TestCase):
def parsesSurveyPassedInAsCsvAndReturnsXmlRepresentation(self):
text = """"survey",,,,,
,"name","type","label","hint","required"
,"gps","geopoint","Record your current location",,"false"
,"start","start",,,
,"end","end",,,
"settings",
,"form_title"
,"New survey" """
xml = create_survey_from_csv_text(text)
lxml.fromstring(xml)
when I run my module with python manage.py test, the output is
Ran 0 tests in 0.000s
I know the runner is picking up the file because if I make an invalid import it throws an error.
Why is the test not being called?
The name of the test methods need to start with test_. This allows the class to have both test methods and helper methods that you may write as well.
Hence you should rename your method to test_parsesSurveyPassedInAsCsvAndReturnsXmlRepresentation (and perhaps shorten the name too).

Run Python/Django Management Command from a UnitTest/WebTest

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

Categories

Resources