{"id":1701,"date":"2019-11-25T15:48:46","date_gmt":"2019-11-25T13:48:46","guid":{"rendered":"https:\/\/djangostars.com\/blog\/?p=1701"},"modified":"2025-09-12T16:02:49","modified_gmt":"2025-09-12T16:02:49","slug":"django-pytest-testing","status":"publish","type":"post","link":"https:\/\/djangostars.com\/blog\/django-pytest-testing\/","title":{"rendered":"Testing Your Django App With Pytest"},"content":{"rendered":"<blockquote>\n<p style=\"text-align: right;\">Helps you write better programs.<br \/>\n\u2014\u00a0<a href=\"https:\/\/docs.pytest.org\/en\/latest\/index.html#pytest-helps-you-write-better-programs\" rel=\"nofollow\">pytest<\/a><\/p>\n<\/blockquote>\n<p>Many developers from Python community heard of and used unit testing to test their projects and knew about boilerplate code with Python and Django unittest module. But Pytest and Django suggest much more pythonic tests without boilerplate.<\/p>\n<p>In this comprehensive tutorial, we are talking about PyTest in Python. We have sufficient experience in this topic (what is our <a href=\"https:\/\/djangostars.com\/case-studies\/\">portfolio<\/a> worth), so if you need help or additional advice, please <a href=\"https:\/\/djangostars.com\/get-in-touch\/\">contact us<\/a>.<\/p>\n<h2>Why You Should Use Pytest?<\/h2>\n<p>While you can get by without it when testing in Python and Django, Pytest provides a new approach for writing tests, namely, functional testing for applications and libraries. Below I\u2019ll list some pros and cons about this framework. We often use this in Django development.<\/p>\n<h3>Pros of Using Pytest:<\/h3>\n<p>&#8211; <a href=\"https:\/\/docs.pytest.org\/en\/latest\/how-to\/assert.html#assert\" rel=\"nofollow\">Assert statements<\/a> (no need to remember `self.assert*` names)<br \/>\n&#8211; Detailed info on failures<br \/>\n&#8211; <a href=\"https:\/\/docs.pytest.org\/en\/latest\/explanation\/fixtures.html#fixture\" rel=\"nofollow\">Fixtures<\/a> (explicit, modular, scalable)<br \/>\n&#8211; Additional features of fixtures (auto-use, scope, request object, nested, finalizers, etc.)<br \/>\n&#8211; <a href=\"https:\/\/docs.pytest.org\/en\/latest\/explanation\/goodpractices.html#test-discovery\" rel=\"nofollow\">Auto-discovery<\/a> of test modules and functions<br \/>\n&#8211; <a href=\"https:\/\/doc.pytest.org\/en\/latest\/how-to\/mark.html#mark\" rel=\"nofollow\">Marks<\/a><br \/>\n&#8211; <a href=\"https:\/\/docs.pytest.org\/en\/latest\/how-to\/parametrize.html\" rel=\"nofollow\">Parametrizing<\/a><br \/>\n&#8211; Less boilerplate code: just create file, write function with assert and run (simple is better than complex)<br \/>\n&#8211; No Camel \u0441ase as PyUnit<br \/>\n&#8211; Plugins with over 736+external plugins and thriving community<br \/>\n&#8211; Can run <a href=\"https:\/\/docs.pytest.org\/en\/latest\/how-to\/unittest.html#unittest\" rel=\"nofollow\">unittest<\/a> and <a href=\"https:\/\/docs.pytest.org\/en\/latest\/how-to\/nose.html#noseintegration\" rel=\"nofollow\">nose<\/a> test suites out of the box<br \/>\n&#8211; Python 3.5+ and PyPy 3<\/p>\n<h3>Cons of Using Pytest:<\/h3>\n<p>&#8211; Requires a bit more advanced Python knowledge than using <a href=\"https:\/\/docs.python.org\/3\/library\/unittest.html\" rel=\"nofollow\">unittest<\/a>, like <a href=\"https:\/\/peps.python.org\/pep-0318\/\" rel=\"nofollow\">decorators<\/a> and <a href=\"https:\/\/peps.python.org\/pep-0255\/\" rel=\"nofollow\">simple generators<\/a><br \/>\n&#8211; The need for a separate installation of the module. But it can be an advantage as well, because you don\u2019t depend on Python version. If you want new features, you just need to update pytest package.<\/p>\n<h2>Short Introduction to Pytest<\/h2>\n<p>First of all, I would like to make a small intro to pytest philosophy and base syntax. I will do it in the format of the most frequently asked questions and answers. This is a very short intro for pytest with basic functionality, but make sure to check it as we will use it in the next parts.<\/p>\n<p><strong>Q1: What are Pytest Fixtures?<\/strong><\/p>\n<p>Fixtures are functions that run before and after each test, like setUp and tearDown in unitest and labelled pytest killer feature. Fixtures are used for data configuration, connection\/disconnection of databases, calling extra actions, etc.<\/p>\n<p>All fixtures have scope argument with available values:<\/p>\n<ul>\n<li><strong>function<\/strong> run once per test<\/li>\n<li><strong>class<\/strong> run once per class of tests<\/li>\n<li><strong>module<\/strong> run once per module<\/li>\n<li><strong>session<\/strong> run once per session<\/li>\n<\/ul>\n<p style=\"padding-left: 40px;\"><strong>Note:<\/strong> Default value of scope is <em>function<\/em>.<\/p>\n<p>Example of simple fixture creation:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/b380c8f8734e3705632b98d62c28820b.js\"><\/script><br \/>\nAnother kind of fixture is yield fixture which provides access to test before and after the run, analogous to <code>setUp<\/code> and <code>tearDown<\/code>.<\/p>\n<p>Example of simple <code>yield<\/code> fixture creation:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/8a9a817c441d010e7036eb86097a5a34.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\"><strong>Note:<\/strong> Normal fixtures can use <code>yield<\/code> directly so the <code>yield_fixture<\/code> decorator is no longer needed and is considered deprecated.<\/p>\n<p><strong>Q2: How to use Fixtures with tests in Pytest?<\/strong><\/p>\n<p>To use fixture in test, you can put fixture name as function argument:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/750bf9302164b070529343f6f97977c5.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\"><strong>Note: Pytest automatically register our fixtures<\/strong> and can have access to fixtures without extra imports.<\/p>\n<p><strong>Q3: What is Marks in Pytest?<\/strong><\/p>\n<p>Marks is a helper using which you can easily set metadata on your test functions, some builtin markers, for example:<\/p>\n<ul>\n<li><a href=\"https:\/\/doc.pytest.org\/en\/latest\/how-to\/skipping.html#skip\" rel=\"nofollow\">skip<\/a> &#8211; always skip a test function<\/li>\n<li><a href=\"https:\/\/doc.pytest.org\/en\/latest\/how-to\/skipping.html#xfail\" rel=\"nofollow\">xfail<\/a> &#8211; produce an \u201cexpected failure\u201d outcome if a certain condition is met<\/li>\n<\/ul>\n<p>Example of used marks:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/daa3196b48cda16088a9d3a53192178b.js\"><\/script><br \/>\n<strong>Q4: How to create custom Marks for Pytest?<\/strong><\/p>\n<p>The one way is to register your marks in <code>pytest.ini<\/code> file:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/3535470a118eeb0ff040901f5a1a5c35.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\"><strong>Note<\/strong>: Everything after the \u201c:\u201d is an optional description for mark<\/p>\n<p><strong>Q5: How to run test with Marks in Pytest?<\/strong><\/p>\n<p>You can run all tests <code>xfail<\/code> without slowing down marks with the next command:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/c9971e6944de9d00cd2708b7aee0fcc1.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\"><strong>Note:<\/strong> when the \u2018&#8211;strict-markers\u2019 command-line flag is passed, any unknown marks applied with the \u2018@pytest.mark.name_of_the_mark\u2019 decorator will trigger an error.<\/p>\n<p><strong>Q6: What is Parametrize in Pytest?<\/strong><\/p>\n<p>`Parametrize` is a builtin mark and one of the killer features of pytest. With this mark, you can perform multiple calls to the same test function.<\/p>\n<p>Example of simple <code>parametrize<\/code> in test:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/b9c8f03e802c8e5a76d0134e47a84a93.js\"><\/script><br \/>\nThat\u2019s pretty much it. Further, we\u2019ll work with these basics to set up pytest for your Django project.<\/p>\n<h2>Setting up Pytest for Django Project<\/h2>\n<p>For testing our Django applications with pytest we won\u2019t reinvent the wheel and will use existing plugin <a href=\"https:\/\/pytest-django.readthedocs.io\/en\/latest\/index.html\" rel=\"nofollow\">pytest-django<\/a>, that provides a set of useful tools for testing Django apps and projects. Let\u2019s start with configuration plugin. If you\u2019re weighing <a href=\"https:\/\/djangostars.com\/blog\/flask-vs-django\/\">Flask vs Django<\/a>, both work smoothly with pytest.<\/p>\n<h3>1. Installation<\/h3>\n<p>Pytest can be installed with <code>pip<\/code><br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/2418251bd88a05d58f67fcfff741d0fd.js\"><\/script><br \/>\nInstalling pytest-django will also automatically install the latest version of pytest. pytest-django uses pytest\u2019s plugin system and can be used right away after installation, there is nothing more to configure.<\/p>\n<h3>2. Point your Django settings to pytest<\/h3>\n<p>You need to tell pytest which Django settings should be used for test runs. The easiest way to achieve this is to create a pytest configuration file with this information.<br \/>\nCreate a file called <code>pytest.ini<\/code> in your project root directory that contains:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/422f670851529d7c2a5a45ce3d1e4ca4.js\"><\/script><br \/>\nWhen using Pytest, Django settings can also be specified by setting the <code>DJANGO_SETTINGS_MODULE<\/code> environment variable or specifying the <code>--ds=yourproject.settings<\/code> command-line flag when running the tests. See the full documentation on <a href=\"https:\/\/pytest-django.readthedocs.io\/en\/latest\/configuring_django.html#configuring-django-settings\">Configuring Django settings<\/a>. On most <a href=\"https:\/\/djangostars.com\/blog\/top-django-compatible-hosting-services\/\">Django hosting<\/a> setups, mirror these environment variables in CI\/staging so tests run against the same configuration you deploy.<\/p>\n<p>Optionally, also add the following line to the <code>[pytest]<\/code> section to instruct pytest to collect tests in Django\u2019s default app layouts too.<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/451bb45eb193d9bcd91a2668c6114cb0.js\"><\/script><\/p>\n<h3>3. Run your test suite<\/h3>\n<p>Tests are invoked directly with the pytest command, instead of manage.py test that you might be used to:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/7ba301e7e8018edd0a440206005ab03c.js\"><\/script><br \/>\nSpecific test files or directories or single test can be selected by specifying the test file names directly on the command line:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/9cca3f221a86cdf963828c4d54c2625f.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\"><strong>Note:<\/strong> You may wonder \u201cwhy would I use this instead of Django <code>manage.py<\/code> test command\u201d? It\u2019s easy. Running the test suite with <code>pytest<\/code> for Django offers some features that are not present in Django standard test mechanism:<\/p>\n<ul>\n<li>Less boilerplate: no need to import unittest, create a subclass with methods. Just write tests as regular functions.<\/li>\n<li>Manage test dependencies with fixtures.<\/li>\n<li>Run tests in multiple processes for increased speed.<\/li>\n<li>There are a lot of other nice plugins available for pytest.<\/li>\n<li>Easy switching: Existing unittest-style tests will still work without any modifications.<\/li>\n<\/ul>\n<p>For now, we are configured and ready for writing first test with <code>pytest<\/code> and Django.<\/p>\n<h2>Django Testing with Pytest<\/h2>\n<h3>1. Database Helpers<\/h3>\n<p>To gain access to the database pytest-django get <code>django_db<\/code> mark or request one of the <code>db<\/code>, <code>transactional_db<\/code> or <code>django_db_reset_sequences<\/code> fixtures.<\/p>\n<p style=\"padding-left: 40px;\"><strong>Note:<\/strong> all these database access methods automatically use <code>django.test.TestCase<\/code><\/p>\n<p style=\"padding-left: 40px;\"><strong>django_db:<\/strong> to get access to the Django test database, each test will run in its own transaction that will be rolled back at the end of the test. Just like it happens in <code>django.test.TestCase<\/code>. We\u2019ll use it constantly, because Django needs access to DB.<\/p>\n<p><script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/8c4fca6127b34e04c0196f31050ba2bf.js\"><\/script><br \/>\nIf you want to get access to the Django database inside a fixture this marker will not help even if the function requesting your fixture has this marker applied. To access the database in a fixture, the fixture itself will have to request the <code>db<\/code>, <code>transactional_db<\/code> or <code>django_db_reset_sequences<\/code> fixture. Below you may find a description of each one.<\/p>\n<p style=\"padding-left: 40px;\"><strong>db:<\/strong> This fixture will ensure the Django database is set up. Only required for fixtures that want to use the database themselves. A test function should normally use the <code>pytest.mark.django_db<\/code> mark to signal it needs the database.<\/p>\n<p style=\"padding-left: 40px;\"><strong>transactional_db:<\/strong> This fixture can be used to request access to the database including transaction support. This is only required for fixtures which need database access themselves. A test function should normally use the <code>pytest.mark.django_db<\/code> mark with <code>transaction=True<\/code>.<\/p>\n<p style=\"padding-left: 40px;\"><strong>django_db_reset_sequences:<\/strong> This fixture provides the same transactional database access as <code>transactional_db<\/code>, with additional support for reset of auto increment sequences (if your database supports it). This is only required for fixtures which need database access themselves. A test function should normally use the <code>pytest.mark.django_db<\/code> mark with <code>transaction=True<\/code> and <code>reset_sequences=True<\/code>.<\/p>\n<h3>2. Client<\/h3>\n<p>The more frequently used thing in Django unit testing is <code>django.test.client<\/code>, because we use it for each request to our app, pytest-django has a build-in fixture client:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/3e2cfb84a23a45b681bec9216303baea.js\"><\/script><\/p>\n<h3>3. Admin Client<\/h3>\n<p>To get a view with superuser access, we can use <code>admin_client<\/code>, which gives us client with login <code>superuser<\/code>:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/14e36808f197082e9e735b8d5683bc28.js\"><\/script><\/p>\n<h3>4. Create User Fixture<\/h3>\n<p>To create a user for our test we have two options:<\/p>\n<p style=\"padding-left: 40px;\">1) Use Pytest Django Fixtures:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/e50e6d38a9d9279140da821543abb925.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\"><strong>django_user_model:<\/strong> pytest-django helper for shortcut to the User model configured for use by the current Django project, like <code>settings.AUTH_USER_MODEL<\/code><\/p>\n<p><strong>Cons of this option:<\/strong><\/p>\n<ul>\n<li>must be copied for each test<\/li>\n<li>doesn\u2019t allow to set difference fields, because fixture creates User instance instead of us<\/li>\n<\/ul>\n<p><script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/127274a0f2b6a31e9c5a459b29a75581.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\"><strong>admin_user:<\/strong> pytest-django helper instance of a superuser, with username \u201cadmin\u201d and password \u201cpassword\u201d (in case there is no \u201cadmin\u201d user yet).<\/p>\n<p style=\"padding-left: 40px;\">2) Create own Fixture:<\/p>\n<p>To fix the disadvantages listed above we create our own custom fixture:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/cadcfab27a86615e5e14e52815c58e62.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\"><strong>Note:<\/strong> Create user with call to local functions to pass extra arguments as kwargs, because pytest fixture can\u2019t accept arguments.<\/p>\n<p>Re-write tests above:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/e50a10bb9d468aa11241a52fd078ceef.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\"><strong>create_user:<\/strong> basic example how you can make own fixture, we can expand this fixture with two other for example, like <code>create_base_user<\/code> (for base user) and <code>create_superuser<\/code> (with fillable is_staff, is_superuser and etc.fields).<\/p>\n<h3>5. Auto Login Client<\/h3>\n<p>Let\u2019s test some authenticated endpoint:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/a848aa7fc7b1775d99ac568ad145f4a1.js\"><\/script><br \/>\nThe major disadvantage of this method is that we must copy the login block for each test.<br \/>\nLet\u2019s create our own fixture for auto login user:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/1e60aef2df64b9601dcfe8f99430b135.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\"><strong>auto_login_user:<\/strong> own fixture, that takes user as parameter or creates a new one and logins it to client fixture. And at the end it returns client and user back for the future actions.<\/p>\n<p>Use our new fixture for the test above:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/a37b2f39921f24c09ec3c1d6b6486526.js\"><\/script><\/p>\n<h3>6. Parametrizing your tests with Pytest<\/h3>\n<p>Let\u2019s say we must test very similar functionality, for example, different languages.<br \/>\nPreviously, you had to do single tests, like:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/a366ca3efdb63c44727df19baf8e803e.js\"><\/script><\/p>\n<blockquote>\n<p style=\"text-align: right;\">It\u2019s very funny to copy paste your test code, but not for a long time.<br \/>\n\u2014 Andrew Svetlov<\/p>\n<\/blockquote>\n<p>To fix it, pytest has parametrizing fixtures feature. After upgrade we had next tests:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/b91c1eb3a5336680037cd639211cbe48.js\"><\/script><br \/>\nYou can see how much easier and less boilerplate our code has become.<\/p>\n<h3>7. Test Mail Outbox with Pytest<\/h3>\n<p>For testing your mail outbox pytest-django has a built-in fixture <code>mailoutbox<\/code>:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/7ca6227ca6eea843334583ab6b240865.js\"><\/script><br \/>\nFor this test we use our own <code>auto_login_user<\/code> fixture and <code>mailoutbox<\/code> pytest built-in fixture.<br \/>\nTo summarize the advantages of the approach demonstrated above: pytest teaches us how to setup our tests easily, so we could be more focused on testing main functionality.<\/p>\n<h2>Testing Django REST Framework with Pytest<\/h2>\n<h3>1. API Client<\/h3>\n<p>The first thing to do here is to create your own fixture for API Client of PyTest-Django REST Framework:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/e245e0c0c129fdaa8b6e709f43ed2671.js\"><\/script><br \/>\nNow we have <code>api_client<\/code> for our tests:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/63eb87f77ed941c33497089b69ba9535.js\"><\/script><\/p>\n<h3>2. Get or Create Token<\/h3>\n<p>For getting authorized, your API <code>users<\/code> usually use Token. Let\u2019s create fixture to get or create token for a user:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/ca5dac14ba79e9876068a90889041ac8.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\"><strong>get_or_create_token:<\/strong> inheritance <code>create_user<\/code><\/p>\n<p><script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/8450f92503af0bc7c572dac22a46f6b6.js\"><\/script><\/p>\n<h3>3. Auto Credentials<\/h3>\n<p>The test demonstrated above is a good example, but setting credentials for each test will end up in a boilerplate code. And we can use other APIClient method to bypass pytest authentication entirely.<br \/>\nWe can use <code>yield<\/code> feature to extend new fixture:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/5a210b8b5e7e39d1fb3cf9a016d27082.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\"><strong>api_client_with_credentials:<\/strong> inheritance <code>create_user<\/code> and <code>api_client<\/code> fixtures and also clear our credential after every test.<\/p>\n<p><script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/be191b55eeb899b33141454a8f7069eb.js\"><\/script><\/p>\n<h3>4. Data Validation with Pytest Parametrizing<\/h3>\n<p>Most tests for your API endpoint constitute and focus on data validation. You have to create the same tests without counting the difference in several values. We can use pytest <code>parametrizing fixture<\/code> for such solution:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/edfd8a177533fda5b583ad6371c082aa.js\"><\/script><br \/>\nBy that mean, we test many cases with one test function thanks to this outstanding pytest feature.<\/p>\n<h3>5. Mock Extra Action in your Views<\/h3>\n<p>Let\u2019s demonstrate how `unittest.mock` can be used with our test use-case. I\u2019d rather use \u2018unittest.mock\u2019 than \u2018monkeypatch\u2019 fixture. Alternatively, you can use <a href=\"https:\/\/pypi.org\/project\/pytest-mock\/\" rel=\"nofollow\">pytest-mock<\/a> package as it has some useful built-in methods like: <code>assert_called_once() , assert_called_with(*args,**kwargs) , assert_called() and assert_not_called() <\/code>.<br \/>\nIf you want to take a closer look at <code>monkeypatch<\/code> fixture you may check <a href=\"https:\/\/docs.pytest.org\/en\/latest\/how-to\/monkeypatch.html\" rel=\"nofollow\">official documentation<\/a> page.<br \/>\nFor example, we have a third-party service call after we saved our data:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/23fd2e68b3ac89d478cadd78baf5aa5d.js\"><\/script><br \/>\nWe want to test our endpoint without extra request to service and we can use mock.patch as decorator with Pytest test:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/d27873558b27eb1371eee644e2f80f86.js\"><\/script><\/p>\n<h2>Useful Tips for Pytest<\/h2>\n<h3>1. Using Factory Boy as fixtures for testing your Django model<\/h3>\n<p>There are several ways to create Django Model instance for test and example with fixture:<\/p>\n<ul>\n<li><strong>Create object manually<\/strong> \u200a\u2014 \u200atraditional variant: <em>\u201ccreate test data by hand and support it by hand\u201d<\/em><\/li>\n<\/ul>\n<p><script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/d26746b236f2a0f218cf81c740cae5bf.js\"><\/script><br \/>\nIf you want to add other fields like relation with Group, your fixture will get more complex and every new required field will change your fixture:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/a645c2d6775af7254c9b981f80a4e763.js\"><\/script><\/p>\n<ul>\n<li><strong>Django fixtures\u200a<\/strong> \u2014\u200a <em>slow and hard to maintain\u2026 avoid them!<\/em><\/li>\n<\/ul>\n<p>Below I provide an example for comparison:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/dc0cd627210f0eb583b4845bc3a389db.js\"><\/script><br \/>\nCreate fixture that loads fixture data to your session:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/0cfdd59c67948045282a0f1b47c4cee4.js\"><\/script><\/p>\n<ul>\n<li><strong>Factories<\/strong> \u200a\u2014\u200a <em>a solution for creation of your test data in a simple way<\/em>.<br \/>\nI\u2019d prefer to use <a href=\"https:\/\/pytest-factoryboy.readthedocs.io\/en\/latest\/\" rel=\"nofollow\">pytest-factoryboy<\/a> plugin and <a href=\"https:\/\/factoryboy.readthedocs.io\/en\/latest\/\" rel=\"nofollow\">factoryboy<\/a> alongside with Pytest.<br \/>\nAlternatively, you may use <a href=\"https:\/\/model-mommy.readthedocs.io\/en\/latest\/basic_usage.html\" rel=\"nofollow\">model mommy<\/a>.<\/li>\n<\/ul>\n<p style=\"padding-left: 40px;\">1) Install the plugin:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/824494661fba56828e5fac9202280024.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\">2) Create User Factory:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/7b461d6d84e9a36693eb30b93fee213f.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\">3) Register Factory:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/9589d82702abeba6a654c435bced38e4.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\"><strong>Note:<\/strong> Name <strong>convention<\/strong> is a lowercase-underscore class name<\/p>\n<p style=\"padding-left: 40px;\">4) Test your Factory:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/95a7a376fec1b994f889775e8886fd26.js\"><\/script><br \/>\nYou may read more about <a href=\"https:\/\/pytest-factoryboy.readthedocs.io\/en\/latest\/\">pytest-factoryboy<\/a> and <a href=\"https:\/\/factoryboy.readthedocs.io\/en\/latest\/\" rel=\"nofollow\">factoryboy<\/a>.<\/p>\n<h3>2. Improve your Parametrizing tests<\/h3>\n<p>Let\u2019s improve parametrizing test above with some features:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/88ad54d9b3726fe8799e0f77400c3481.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\"><strong>pytest.param: pytest<\/strong> object for setting extra arguments like marks and ids<\/p>\n<p style=\"padding-left: 40px;\"><strong>marks:<\/strong> argument for setting pytest mark<\/p>\n<p style=\"padding-left: 40px;\"><strong>id:<\/strong> argument for setting unique indicator for test<\/p>\n<p style=\"padding-left: 40px;\"><strong>success_request<\/strong> and <strong>bad_request:<\/strong> custom pytest marks<\/p>\n<p>Let\u2019s run our test with some condition:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/9e3696edbe107813e674cb9332a60d65.js\"><\/script><br \/>\nAs a result we have:<br \/>\n&#8211; Collected test with one of bad_request marks<br \/>\n&#8211; Ignore test without pytest.param object, because that don\u2019t have marks parameters<br \/>\n&#8211; Show test with custom ID in console<\/p>\n<h3>3. Mocking your Pytest test with fixture<\/h3>\n<p>Using <a href=\"https:\/\/pypi.org\/project\/pytest-mock\/\">pytest-mock<\/a> plugin is another way to mock your code with pytest approach of naming fixtures as parameters.<\/p>\n<p style=\"padding-left: 40px;\">1) Install the plugin:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/5c801db5a6cc55111cced5dcaf0a20ed.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\">2) Re-write example above:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/b168410770a312d9a0c659950496f544.js\"><\/script><br \/>\nThe mocker is a fixture that has the same API as <code><a href=\"https:\/\/docs.python.org\/3\/library\/unittest.mock.html#patch\">mock.patch<\/a><\/code> and supports the same methods as:<\/p>\n<ul>\n<li>mocker.patch<\/li>\n<li>mocker.patch.object<\/li>\n<li>mocker.patch.multiple<\/li>\n<li>mocker.patch.dict<\/li>\n<li>mocker.stopall<\/li>\n<\/ul>\n<h3>4. Running tests simultaneously<\/h3>\n<p>To speed up your tests, you can run them simultaneously. This can result in significant speed improvements on multi core\/multi CPU machines. It\u2019s possible to realize with <a href=\"https:\/\/pypi.org\/project\/pytest-xdist\/\" rel=\"nofollow\">pytest-xdist<\/a> plugin which expands pytest functionality<\/p>\n<p style=\"padding-left: 40px;\">1) Install the plugin:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/c05420ccc3691100a6d0c2f069b5ef1f.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\">2) Running test with multiprocessing:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/363865c12fe9287be153fe93b7c38f63.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\"><strong>Notes:<\/strong><\/p>\n<p style=\"padding-left: 40px;\">&#8211; Avoid output and stdout executions in your tests, this will result in considerable speed-ups<\/p>\n<p style=\"padding-left: 40px;\">&#8211; When tests are invoked with xdist, pytest-django will create a separate test database for each process. Each test database will be given a suffix (something like <strong>gw0<\/strong>, <strong>gw1<\/strong>) to map to a xdist process. If your database name is set to <strong>foo<\/strong>, the test database with xdist will be <strong>test_foo_gw0<\/strong>, <strong>test_foo_gw1<\/strong>, etc.<\/p>\n<h3>5. Config pytest.ini file<\/h3>\n<p>Example of <code>pytest.in<\/code>i file for your Django project:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/e18e9f6f0079f9a8fba2429210e63db3.js\"><\/script><br \/>\n<strong><code>DJANGO_SETTINGS_MODULE<\/code><\/strong> and <strong><code>python_files<\/code><\/strong> we discussed at the beginning of the article, let\u2019s discover other useful options:<\/p>\n<ul>\n<li><strong>addopts<\/strong><br \/>\nAdd the specified OPTS to the set of command-line arguments as if they had been specified by the user. We\u2019ve specified next options:<\/li>\n<\/ul>\n<p style=\"padding-left: 40px;\"><code><strong><em>--p no:warnings<\/em><\/strong><\/code> \u2014 disables warning capture entirely (this might be useful if your test suites handle warnings using an external system)<\/p>\n<p style=\"padding-left: 40px;\"><code><em><strong>--strict-markers<\/strong><\/em><\/code> \u2014 typos and duplication in function markers are treated as an error<\/p>\n<p style=\"padding-left: 40px;\"><code><em><strong>--no-migrations<\/strong><\/em><\/code> will disable Django migrations and create the database by inspecting all Django models. It may be faster when there are several migrations to run in the database setup.<\/p>\n<p style=\"padding-left: 40px;\"><code><strong><em>--reuse-db<\/em><\/strong><\/code> reuses the testing database between test runs. It provides much faster startup time for tests.<\/p>\n<p style=\"padding-left: 40px;\"><strong>Exemplary workflow with<\/strong> <code>--reuse-db<\/code> and <code>--create-db<\/code>:<\/p>\n<p style=\"padding-left: 40px;\">&#8211;\u00a0 run tests with <code>pytest<\/code>; on the first run the test database will be created. On the next test run it will be reused.<\/p>\n<p style=\"padding-left: 40px;\">&#8211; when you alter your database schema, run <code>pytest --create-db<\/code> to force re-creation of the test database.<\/p>\n<ul>\n<li><strong>norecursedirs<\/strong><br \/>\nSet the exclusion of directory basename patterns when recursing for test discovery. This will tell pytest not to look into <code>venv<\/code> and <code>old_testsdirectory<\/code><\/li>\n<\/ul>\n<p style=\"padding-left: 40px;\"><strong>Note: <\/strong>Default patterns are <code>'.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg', 'venv'<\/code><\/p>\n<ul>\n<li><strong>markers<\/strong><br \/>\nYou can list additional markers in this setting to add them to the whitelist and use them in your tests.<\/li>\n<\/ul>\n<p>Run all tests with mark <strong>slow<\/strong>:<br \/>\n<script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/3a30cb288c4de1acc4191ab9f3f315f6.js\"><\/script><\/p>\n<h3>6. Show your coverage of the test<\/h3>\n<p>To check the Django pytest coverage of your Python app you can use <a href=\"https:\/\/pypi.org\/project\/pytest-cov\/\" rel=\"nofollow\">pytest-cov<\/a> plugin<\/p>\n<p style=\"padding-left: 40px;\">1) Install plugin:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/3c5ea8e0a7a7f45a4265f2908e5317d1.js\"><\/script><\/p>\n<p style=\"padding-left: 40px;\">2) Coverage of your project and example of report:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/dchuvardynskyi\/e75a7165020b79603517aacf52c2569b.js\"><\/script><br \/>\nTo wrap up, using this guide now you can avoid boilerplate code in your tests and make them smoother with Pytest. I hope that this post helped you to explore possibilities of Pytest deeper and bring your coding skills to the next level. <strong>And if you need consulting or a dedicated team extension, contact <a href=\"https:\/\/djangostars.com\/services\/python-django-development\/\">Django web development company<\/a>.<\/strong><div class=\"lead-form-wrapper lets_disqus\">\n    <div class=\"lead-form transparent-footer\">\n        <p class=\"discuss-title paragraph-discuss col-md-12\">Have an idea? Let&#039;s discuss!<\/p>\n\n        \n<div class=\"wpcf7 no-js\" id=\"wpcf7-f2589-o1\" lang=\"en-US\" dir=\"ltr\" data-wpcf7-id=\"2589\">\n<div class=\"screen-reader-response\"><p role=\"status\" aria-live=\"polite\" aria-atomic=\"true\"><\/p> <ul><\/ul><\/div>\n<form action=\"\/blog\/wp-json\/wp\/v2\/posts\/1701#wpcf7-f2589-o1\" method=\"post\" class=\"wpcf7-form init\" aria-label=\"Contact form\" enctype=\"multipart\/form-data\" novalidate=\"novalidate\" data-status=\"init\">\n<div style=\"display: none;\">\n<input type=\"hidden\" name=\"_wpcf7\" value=\"2589\" \/>\n<input type=\"hidden\" name=\"_wpcf7_version\" value=\"6.0.6\" \/>\n<input type=\"hidden\" name=\"_wpcf7_locale\" value=\"en_US\" \/>\n<input type=\"hidden\" name=\"_wpcf7_unit_tag\" value=\"wpcf7-f2589-o1\" \/>\n<input type=\"hidden\" name=\"_wpcf7_container_post\" value=\"0\" \/>\n<input type=\"hidden\" name=\"_wpcf7_posted_data_hash\" value=\"\" \/>\n<input type=\"hidden\" name=\"form_start_time\" value=\"1775579882\" \/>\n<input type=\"hidden\" name=\"_wpcf7_recaptcha_response\" value=\"\" \/>\n<\/div>\n<div class=\"form_holder\">\n    <div class=\"input_section input_row\">\n        <div class=\"input_holder\">\n                            <span class=\"input_label\">\n                               Your name *\n                            <\/span>\n            <input size=\"40\" maxlength=\"400\" class=\"wpcf7-form-control wpcf7-text wpcf7-validates-as-required\" id=\"your-name\" aria-required=\"true\" aria-invalid=\"false\" value=\"\" type=\"text\" name=\"text-898\" \/>\n\n            <input class=\"wpcf7-form-control wpcf7-hidden\" id=\"uniq_ga_id\" value=\"\" type=\"hidden\" name=\"uniq_ga_id\" \/>\n        <\/div>\n        <div class=\"input_holder\">\n                            <span class=\"input_label\">\n                                Your email *\n                            <\/span>\n            <input size=\"40\" maxlength=\"400\" class=\"wpcf7-form-control wpcf7-email wpcf7-validates-as-required wpcf7-text wpcf7-validates-as-email\" id=\"your-email\" aria-required=\"true\" aria-invalid=\"false\" value=\"\" type=\"email\" name=\"email-882\" \/>\n        <\/div>\n    <\/div>\n    <div class=\"input_section single_input_row\">\n        <div class=\"input_holder\">\n            <span class=\"input_label\">How can we help you? *<\/span>\n            <input size=\"40\" maxlength=\"400\" class=\"wpcf7-form-control wpcf7-text\" id=\"message\" aria-invalid=\"false\" value=\"\" type=\"text\" name=\"message\" \/>\n        <\/div>\n    <\/div>\n    <div class=\"file_attach\">\n        <input size=\"40\" class=\"wpcf7-form-control wpcf7-file\" accept=\"audio\/*,video\/*,image\/*\" aria-invalid=\"false\" type=\"file\" name=\"file-930\" \/>\n        <div class=\"file_placeholder\">\ud83d\udcce <span>Attach File<\/span>\n            <span class=\"file_formats\">Formats: pdf, doc, docx, rtf, ppt, pptx.<\/span><\/div>\n    <\/div>\n    <div class=\"checkbox_row\">\n        <div class=\"single_checkbox\"><div class=\"checkbox_indicator\"><div class=\"checked_indicator\"><svg width=\"14\" height=\"12\" viewBox=\"0 0 14 12\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M4.66804 12L0 7.26951L1.22426 6.05269L4.54927 9.40456L12.6737 0L14 1.10613L4.66804 12Z\" fill=\"#1E232C\"\/><\/svg><\/div><\/div><input type=\"checkbox\" name=\"agree\" id=\"privacy\" value=\"agree privacy\"><label for=\"privacy\" class=\"\">I have read and accepted <a href=\"https:\/\/djangostars.com\/privacy-policy\/\" style=\"margin-left: 6px;\"> Privacy Policy*<\/a><\/label><\/div>\n        <div class=\"single_checkbox\"><div class=\"checkbox_indicator\"><div class=\"checked_indicator\"><svg width=\"14\" height=\"12\" viewBox=\"0 0 14 12\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M4.66804 12L0 7.26951L1.22426 6.05269L4.54927 9.40456L12.6737 0L14 1.10613L4.66804 12Z\" fill=\"#1E232C\"\/><\/svg><\/div><\/div><input type=\"checkbox\" name=\"agree\" id=\"marketing\" value=\"agree for marketing\"><label for=\"marketing\" class=\"\">I agree to receive marketing content from Django Stars<\/label><\/div>\n    <\/div>\n    <div class=\"submit\"><button type=\"submit\"><span>send message<\/span><\/button><div class=\"safeguard\">We safeguard your privacy<\/div><\/div>\n<\/div>\n<div style=\"position: absolute; left: -5000px;\" aria-hidden=\"true\">\n    <input size=\"40\" maxlength=\"400\" class=\"wpcf7-form-control wpcf7-text\" aria-invalid=\"false\" value=\"\" type=\"text\" name=\"website_url\" \/>\n<\/div><script type='text\/javascript'>\n\n\t\t\t\t\t\tif(contactform === undefined){\n\t\t\t\t\t\t\tvar contactform = [];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tvar innerVal = [2589,'mail_sent_ok','Thank you for your message. It has been sent.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'mail_sent_ng','There was an error trying to send your message. Please try again later.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'validation_error','One or more fields have an error. Please check and try again.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'spam','There was an error trying to send your message. Please try again later.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'accept_terms','You must accept the terms and conditions before sending your message.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'invalid_required','The field is required.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'invalid_too_long','The field is too long.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'invalid_too_short','The field is too short.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'upload_failed','There was an unknown error uploading the file.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'upload_file_type_invalid','You are not allowed to upload files of this type.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'upload_file_too_large','The file is too big.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'upload_failed_php_error','There was an error uploading the file.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'invalid_date','The date format is incorrect.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'date_too_early','The date is before the earliest one allowed.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'date_too_late','The date is after the latest one allowed.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'invalid_number','The number format is invalid.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'number_too_small','The number is smaller than the minimum allowed.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'number_too_large','The number is larger than the maximum allowed.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'quiz_answer_not_correct','The answer to the quiz is incorrect.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'invalid_email','The e-mail address entered is invalid.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'invalid_url','The URL is invalid.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'invalid_tel','The telephone number is invalid.'];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\tvar innerVal = [2589,'gdpr',''];\n\t\t\t\t\t\tcontactform.push(innerVal);\n\t\t\t\t\t\t<\/script><div class=\"wpcf7-response-output\" aria-hidden=\"true\"><\/div>\n<\/form>\n<\/div>\n    <\/div>\n\n    <div class=\"success_disqus\">\n        Thank you for your message.\n        <span>We\u2019ll contact you shortly<\/span>.\n    <\/div>\n<\/div>\n\n<script>\n    \/\/ (function ($) {\n    function click_input() {\n        jQuery('.file_placeholder').on('click', function () {\n            jQuery(this).parent().find('input').click();\n        })\n    }\n\n    document.addEventListener(\"DOMContentLoaded\", click_input);\n\n    \/\/ })(jQuery)\n<\/script>\n\n\n<div class=\"dj-main-article-faq\" style=\"padding-top: 0px;\">\n\t\t<div class=\"dj-main-article-faq-title\">\n\t\tFrequently Asked Questions\n\t\t<\/div>\n\t\t<div class=\"dj-main-article-faq-items\">\n\t\t\t<div class=\"dj-main-article-faq-accordeon accordeon\"><dl>\n\t\t\t\t<dt>Can I use PyTest with Django? \n\t\t\t\t<div class=\"cross\">\n\t\t\t\t<span><\/span>\n\t\t\t\t<span><\/span>\n\t\t\t\t<\/div>\n\t\t\t\t<\/dt>\n\t\t\t\t<dd>Yes, you can use the <b>pytest-django<\/b> plugin maintained by the pytest development team. It allows the use of pytest to write tests for Django projects and provides a set of handy tools for this.<\/dd>\n\t\t\t<\/dl><dl>\n\t\t\t\t<dt>What is the advantage of Pytest? \n\t\t\t\t<div class=\"cross\">\n\t\t\t\t<span><\/span>\n\t\t\t\t<span><\/span>\n\t\t\t\t<\/div>\n\t\t\t\t<\/dt>\n\t\t\t\t<dd>Compared to built-in tools, Pytest uses less boilerplate code. Also, Pytest makes it possible to run multiple tests in parallel, saving time. During execution, it allows skipping a test subset. If some test files and functions are not explicitly specified, Pytest auto-detects them. It can run unittest-based and nose test suites out of the box, provide detailed info on failures, and has many other benefits. <\/dd>\n\t\t\t<\/dl><dl>\n\t\t\t\t<dt>What are the features of Pytest? \n\t\t\t\t<div class=\"cross\">\n\t\t\t\t<span><\/span>\n\t\t\t\t<span><\/span>\n\t\t\t\t<\/div>\n\t\t\t\t<\/dt>\n\t\t\t\t<dd>Pytest's features that make it a powerful and flexible tool for testing Django applications include fixtures (to add arbitrary functions to test execution), marks (to simplify adding metadata), assert statements, parametrizing, auto-detecting of test modules and functions, and detailed info on failures. In addition, the functionality of PyTest can be extended with a large selection of external plugins.<\/dd>\n\t\t\t<\/dl><dl>\n\t\t\t\t<dt>How do I run test py in Django? \n\t\t\t\t<div class=\"cross\">\n\t\t\t\t<span><\/span>\n\t\t\t\t<span><\/span>\n\t\t\t\t<\/div>\n\t\t\t\t<\/dt>\n\t\t\t\t<dd>To run written tests in Django, you can use the <b>test<\/b> command of the project\u2019s <b>manage.py<\/b> utility: <code>$ .\/manage.py test<\/code>. The unittest module's built-in test discovery will discover tests in any file named <b>test*.py<\/b> (by default) under the current working directory. You can read more about this in the <a href=\"https:\/\/docs.djangoproject.com\/en\/4.1\/topics\/testing\/overview\/\" rel=\"nofollow\">Django documentation<\/a>. In Pytest, <b>pytest<\/b> command is used instead of <b>manage.py test<\/b>.<\/dd>\n\t\t\t<\/dl><dl>\n\t\t\t\t<dt>How to test Django REST Framework with Pytest? \n\t\t\t\t<div class=\"cross\">\n\t\t\t\t<span><\/span>\n\t\t\t\t<span><\/span>\n\t\t\t\t<\/div>\n\t\t\t\t<\/dt>\n\t\t\t\t<dd>First, create your own PyTest fixture for API client of Django REST Framework. Then, create fixture to get or create token for a user or API client with credentials to bypass PyTest authentication. Also, you can use PyTest parametrizing fixture for data validation, which helps avoid creating almost identical tests, and mock extra action in your views.<\/dd>\n\t\t\t<\/dl><\/div>\n\t\t\t<\/div>\n\t\t<\/div><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Helps you write better programs. \u2014\u00a0pytest Many developers from Python community heard of and used unit testing to test their projects and knew about boilerplate code with Python and Django unittest module. But Pytest and Django suggest much more pythonic tests without boilerplate. In this comprehensive tutorial, we are talking about PyTest in Python. We [&hellip;]<\/p>\n","protected":false},"author":29,"featured_media":3547,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[44,70],"tags":[24],"class_list":["post-1701","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-python-django","category-qa","tag-quality-assurance"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Software Development Blog &amp; IT Tech Insights | Django Stars<\/title>\n<meta name=\"description\" content=\"It&#039;s easy to avoid boilerplate code when testing in Django. This guide shows how to do testing in Django app with Pytest.\" \/>\n<link rel=\"canonical\" href=\"https:\/\/djangostars.com\/blog\/wp-json\/wp\/v2\/posts\/1701\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Testing Your Django App With Pytest\" \/>\n<meta property=\"og:description\" content=\"Testing in Django avoiding boilerplate code is easier than you think. Check this guide to learn how to do it with Pytest.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/djangostars.com\/blog\/django-pytest-testing\/\" \/>\n<meta property=\"og:site_name\" content=\"Software Development Blog &amp; IT Tech Insights | Django Stars\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/djangostars\/\" \/>\n<meta property=\"article:published_time\" content=\"2019-11-25T13:48:46+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-09-12T16:02:49+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/djangostars.com\/blog\/wp-content\/uploads\/2021\/12\/Testing-Your-Django-App-With-Pytest.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1440\" \/>\n\t<meta property=\"og:image:height\" content=\"620\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Serhii Bolilyi\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:title\" content=\"Testing Your Django App With Pytest\" \/>\n<meta name=\"twitter:description\" content=\"Testing in Django avoiding boilerplate code is easier than you think. Check this guide to learn how to do it with Pytest.\" \/>\n<meta name=\"twitter:creator\" content=\"@djangostars\" \/>\n<meta name=\"twitter:site\" content=\"@djangostars\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Serhii Bolilyi\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/djangostars.com\/blog\/django-pytest-testing\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/djangostars.com\/blog\/django-pytest-testing\/\"},\"author\":{\"name\":\"Serhii Bolilyi\",\"@id\":\"https:\/\/djangostars.com\/blog\/#\/schema\/person\/183a654c18587fc734d2e649ae17dfea\"},\"headline\":\"Testing Your Django App With Pytest\",\"datePublished\":\"2019-11-25T13:48:46+00:00\",\"dateModified\":\"2025-09-12T16:02:49+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/djangostars.com\/blog\/django-pytest-testing\/\"},\"wordCount\":2720,\"commentCount\":0,\"image\":{\"@id\":\"https:\/\/djangostars.com\/blog\/django-pytest-testing\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/djangostars.com\/blog\/wp-content\/uploads\/2021\/12\/Testing-Your-Django-App-With-Pytest.jpg\",\"keywords\":[\"Testing\"],\"articleSection\":[\"Python &amp; Django\",\"QA\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/djangostars.com\/blog\/django-pytest-testing\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/djangostars.com\/blog\/django-pytest-testing\/\",\"url\":\"https:\/\/djangostars.com\/blog\/django-pytest-testing\/\",\"name\":\"How to Test Your Django App With Pytest?\",\"isPartOf\":{\"@id\":\"https:\/\/djangostars.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/djangostars.com\/blog\/django-pytest-testing\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/djangostars.com\/blog\/django-pytest-testing\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/djangostars.com\/blog\/wp-content\/uploads\/2021\/12\/Testing-Your-Django-App-With-Pytest.jpg\",\"datePublished\":\"2019-11-25T13:48:46+00:00\",\"dateModified\":\"2025-09-12T16:02:49+00:00\",\"author\":{\"@id\":\"https:\/\/djangostars.com\/blog\/#\/schema\/person\/183a654c18587fc734d2e649ae17dfea\"},\"description\":\"It's easy to avoid boilerplate code when testing in Django. This guide shows how to do testing in Django app with Pytest.\",\"breadcrumb\":{\"@id\":\"https:\/\/djangostars.com\/blog\/django-pytest-testing\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/djangostars.com\/blog\/django-pytest-testing\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/djangostars.com\/blog\/django-pytest-testing\/#primaryimage\",\"url\":\"https:\/\/djangostars.com\/blog\/wp-content\/uploads\/2021\/12\/Testing-Your-Django-App-With-Pytest.jpg\",\"contentUrl\":\"https:\/\/djangostars.com\/blog\/wp-content\/uploads\/2021\/12\/Testing-Your-Django-App-With-Pytest.jpg\",\"width\":1440,\"height\":620,\"caption\":\"Testing-Your-Django-App-With-Pytest\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/djangostars.com\/blog\/django-pytest-testing\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/djangostars.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Testing Your Django App With Pytest\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/djangostars.com\/blog\/#website\",\"url\":\"https:\/\/djangostars.com\/blog\/\",\"name\":\"Software Development Blog &amp; IT Tech Insights | Django Stars\",\"description\":\"Welcome behind the scenes of software product development. We share our best practices, tech solutions, management tips, and every useful insight we\u2018ve got while working on our projects.\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/djangostars.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/djangostars.com\/blog\/#\/schema\/person\/183a654c18587fc734d2e649ae17dfea\",\"name\":\"Serhii Bolilyi\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/djangostars.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/c0150ee1ecb62c13eb49376f2513c94afacf1a0e078c08468061ebbe11169c9e?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/c0150ee1ecb62c13eb49376f2513c94afacf1a0e078c08468061ebbe11169c9e?s=96&d=mm&r=g\",\"caption\":\"Serhii Bolilyi\"},\"sameAs\":[\"https:\/\/www.linkedin.com\/in\/serhiibolilyi\/\"],\"url\":\"https:\/\/djangostars.com\/blog\/author\/serhii-bolilyi\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Software Development Blog &amp; IT Tech Insights | Django Stars","description":"It's easy to avoid boilerplate code when testing in Django. This guide shows how to do testing in Django app with Pytest.","canonical":"https:\/\/djangostars.com\/blog\/wp-json\/wp\/v2\/posts\/1701","og_locale":"en_US","og_type":"article","og_title":"Testing Your Django App With Pytest","og_description":"Testing in Django avoiding boilerplate code is easier than you think. Check this guide to learn how to do it with Pytest.","og_url":"https:\/\/djangostars.com\/blog\/django-pytest-testing\/","og_site_name":"Software Development Blog &amp; IT Tech Insights | Django Stars","article_publisher":"https:\/\/www.facebook.com\/djangostars\/","article_published_time":"2019-11-25T13:48:46+00:00","article_modified_time":"2025-09-12T16:02:49+00:00","og_image":[{"width":1440,"height":620,"url":"https:\/\/djangostars.com\/blog\/wp-content\/uploads\/2021\/12\/Testing-Your-Django-App-With-Pytest.jpg","type":"image\/jpeg"}],"author":"Serhii Bolilyi","twitter_card":"summary_large_image","twitter_title":"Testing Your Django App With Pytest","twitter_description":"Testing in Django avoiding boilerplate code is easier than you think. Check this guide to learn how to do it with Pytest.","twitter_creator":"@djangostars","twitter_site":"@djangostars","twitter_misc":{"Written by":"Serhii Bolilyi","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/djangostars.com\/blog\/django-pytest-testing\/#article","isPartOf":{"@id":"https:\/\/djangostars.com\/blog\/django-pytest-testing\/"},"author":{"name":"Serhii Bolilyi","@id":"https:\/\/djangostars.com\/blog\/#\/schema\/person\/183a654c18587fc734d2e649ae17dfea"},"headline":"Testing Your Django App With Pytest","datePublished":"2019-11-25T13:48:46+00:00","dateModified":"2025-09-12T16:02:49+00:00","mainEntityOfPage":{"@id":"https:\/\/djangostars.com\/blog\/django-pytest-testing\/"},"wordCount":2720,"commentCount":0,"image":{"@id":"https:\/\/djangostars.com\/blog\/django-pytest-testing\/#primaryimage"},"thumbnailUrl":"https:\/\/djangostars.com\/blog\/wp-content\/uploads\/2021\/12\/Testing-Your-Django-App-With-Pytest.jpg","keywords":["Testing"],"articleSection":["Python &amp; Django","QA"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/djangostars.com\/blog\/django-pytest-testing\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/djangostars.com\/blog\/django-pytest-testing\/","url":"https:\/\/djangostars.com\/blog\/django-pytest-testing\/","name":"How to Test Your Django App With Pytest?","isPartOf":{"@id":"https:\/\/djangostars.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/djangostars.com\/blog\/django-pytest-testing\/#primaryimage"},"image":{"@id":"https:\/\/djangostars.com\/blog\/django-pytest-testing\/#primaryimage"},"thumbnailUrl":"https:\/\/djangostars.com\/blog\/wp-content\/uploads\/2021\/12\/Testing-Your-Django-App-With-Pytest.jpg","datePublished":"2019-11-25T13:48:46+00:00","dateModified":"2025-09-12T16:02:49+00:00","author":{"@id":"https:\/\/djangostars.com\/blog\/#\/schema\/person\/183a654c18587fc734d2e649ae17dfea"},"description":"It's easy to avoid boilerplate code when testing in Django. This guide shows how to do testing in Django app with Pytest.","breadcrumb":{"@id":"https:\/\/djangostars.com\/blog\/django-pytest-testing\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/djangostars.com\/blog\/django-pytest-testing\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/djangostars.com\/blog\/django-pytest-testing\/#primaryimage","url":"https:\/\/djangostars.com\/blog\/wp-content\/uploads\/2021\/12\/Testing-Your-Django-App-With-Pytest.jpg","contentUrl":"https:\/\/djangostars.com\/blog\/wp-content\/uploads\/2021\/12\/Testing-Your-Django-App-With-Pytest.jpg","width":1440,"height":620,"caption":"Testing-Your-Django-App-With-Pytest"},{"@type":"BreadcrumbList","@id":"https:\/\/djangostars.com\/blog\/django-pytest-testing\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/djangostars.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Testing Your Django App With Pytest"}]},{"@type":"WebSite","@id":"https:\/\/djangostars.com\/blog\/#website","url":"https:\/\/djangostars.com\/blog\/","name":"Software Development Blog &amp; IT Tech Insights | Django Stars","description":"Welcome behind the scenes of software product development. We share our best practices, tech solutions, management tips, and every useful insight we\u2018ve got while working on our projects.","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/djangostars.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/djangostars.com\/blog\/#\/schema\/person\/183a654c18587fc734d2e649ae17dfea","name":"Serhii Bolilyi","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/djangostars.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/c0150ee1ecb62c13eb49376f2513c94afacf1a0e078c08468061ebbe11169c9e?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/c0150ee1ecb62c13eb49376f2513c94afacf1a0e078c08468061ebbe11169c9e?s=96&d=mm&r=g","caption":"Serhii Bolilyi"},"sameAs":["https:\/\/www.linkedin.com\/in\/serhiibolilyi\/"],"url":"https:\/\/djangostars.com\/blog\/author\/serhii-bolilyi\/"}]}},"_links":{"self":[{"href":"https:\/\/djangostars.com\/blog\/wp-json\/wp\/v2\/posts\/1701","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/djangostars.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/djangostars.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/djangostars.com\/blog\/wp-json\/wp\/v2\/users\/29"}],"replies":[{"embeddable":true,"href":"https:\/\/djangostars.com\/blog\/wp-json\/wp\/v2\/comments?post=1701"}],"version-history":[{"count":30,"href":"https:\/\/djangostars.com\/blog\/wp-json\/wp\/v2\/posts\/1701\/revisions"}],"predecessor-version":[{"id":9698,"href":"https:\/\/djangostars.com\/blog\/wp-json\/wp\/v2\/posts\/1701\/revisions\/9698"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/djangostars.com\/blog\/wp-json\/wp\/v2\/media\/3547"}],"wp:attachment":[{"href":"https:\/\/djangostars.com\/blog\/wp-json\/wp\/v2\/media?parent=1701"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/djangostars.com\/blog\/wp-json\/wp\/v2\/categories?post=1701"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/djangostars.com\/blog\/wp-json\/wp\/v2\/tags?post=1701"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}