Testing a standalone management command

Hi, I’m developing the django-simple-deploy project to help make the initial deployment process much easier for people who are new to Django. Basically, if you have a simple Django project that works locally on your system you can deploy your project in just three steps. The only assumptions are that you’re using Git, you’ve specified your project’s requirements, and you have an account on a platform like Heroku.

I’m unclear on the best way to approach testing the project, because it’s a standalone management command. Currently I have a sample_project/ folder with a simple blog app. I have a test script I wrote in bash that copies the blog project to a tmp directory, and then uses django-simple-deploy just as a user would. It then calls a .py file that uses requests to run a number of tests on the deployed version of the blog project, to ensure that the deployment process worked. There are some improvements I can make to this testing, but overall I’m comfortable with it because it mimics how users interact with the project. This integration test takes minutes to run because it deploys the project to Heroku or Azure. I don’t think there’s any way to speed this up, because I’m limited by the time these platforms take to process a deployment.

My main question is how to approach unit testing the project. The command, python manage.py simple_deploy, needs to run against a Django project. The concept is simple; simple_deploy modifies the project, and I want to verify that those modifications are correct. But I’m not sure how to structure the unit testing overall:

  • Use pytest, and figure out how to bring the sample blog project into the testing framework?
  • Move the blog project up to the root level of the project, and then I can use standard approaches to testing management commands?

The first approach seems complicated because I would have to activate a Django project inside the Django project I’m testing. But the second approach feels like it would really clutter the overall organization of the django-simple-deploy project itself.

Does anyone have any clear thoughts on how to approach this, or suggestions for how to organize the project differently to better support unit testing?

One pattern you could use is creating temporary files within the test suite. I do this fairly often when testing such code.

For example, in my small plugin pytest-is-running, the tests create a temporary test suite with pytest’s tmp_path fixture and then runs pytest in a subprocess on that test suite: Similarly these django-linear-migrations tests create temporary files, run a django management command, and then assert on the outcome.

I almost made myself finish reading my copy of your testing book before posting, in case you replied. :slight_smile:

I think my main confusion about how to approach this comes down to figuring out where this testing should fit between unit testing and integration testing. For example I have a method that adds a heroku-specific setting to settings.py, that sets DEBUG false by default in the deployed version of the project.

It’s probably straightforward to create a tmp settings.py file, and run this method against that file, and verify that the correct setting appears in the settings.py file. But what I really want to do is verify that running simple_deploy against a project parses the project correctly, finds the project’s settings.py file, and adds the appropriate setting to that file.

Should I use pytest to create a copy of the sample blog project in a pytest tmp directory, run the management command against the sample project, and then run a bunch of tests against the new state of the sample project? It’s a straightforward task to do that in bash, which is why I started my testing that way. Would it be reasonable to call a bash script from pytest to do that initial setup, and then do all the inspection in pytest?

The line between unit and integration is blurry. Just test what you need to for confidence.

I’d do it all in Python:

  1. set up the temporary directory with pathlib / pytest’s tmp_path fixture
  2. run your command with subprocess.run, or just by calling main
  3. assert on the file contents using pathlib API