Testing?? What are you talking about!!
After few of my projects crossed critical mass in their code base and complexity they lead me to have a close look at code maintenance and integrity. After addition of each new module you can not just sit there and pray that all of your old stuff will play nice with your new stuff!! You need to MAKE them play nice with new stuff and we don’t have time to look at all the moving parts of our app and assure that they are gonna do that. That is when testing comes into play.
I recently fell in love with something called test driven development. In this kind of development practice you write your test first for the functionality that you gonna implement or the new module you gonna write. So when you actually complete writing your new code you gotta way to make sure that it is gonna play nice with your old code.
Django, Testing and a common man!!
The best thing about Django is that there is all the important stuff just ready to be used by you. All you need to do is open your eyes ( and mind ) towards them and use the code that is written by many amazing people just for you!
Django comes with management command for testing:
>> python manage.py test --------------------------------------------------------------------- Ran 307 tests in 5.763s OK
What? how are there so many tests and all of them passing when you have now written any yet? This are all the django.contrib and other apps that you are using in your Django apps ( in settings.py ). They all have their own tests. But Django apps are gonna be fine what we need to do is test our written apps.
>> python manage.py test <your_app_name>
With this command Django will run tests written by you of your app only.
But wait how will Django know where are your tests written? Well Django will see inside your app folder and will find all .py files whose name start with test_ ( i.e. test_*.py ). So the file that contains all the test of your “poll” app model maybe named as “test_models.py” and you may place it in poll/tests/test_models.py and Django will run all the tests inside that file. Just remember to put an “__init__.py” empty file inside your poll/tests directory so Django consider it as project directory at run time.
Now we know where to put our test files and how Django will find and run it. All we need to know is what tests to write inside those files !!
My two cents on Testing!!
Generally i love unit-testing . In unit-testing you take each small block of code that exhibits a simple feature or functionality and test that single block separately.
Than there is integrity-testing where you test inter relations between those small or simple test blocks of your app and see if they are working as expected together.
For our Django web-app we may like to test our views by testing if each view function returns proper response ( status code 200 / 302 / 404 e.t.c ). For this Django has built-in “django.test.TestCase” class. Lets see a simple example of how you can leverage benefits of this class:
from django.test import TestCase class MyTest(TestCase): """ This class extends TestCase class """ @classmethod def setUpTestData(cls): # setup your test database here # this data can be used to test your views cls.poll = Polls.objects.create(name="new_name") def test_poll(self): # test poll urls and other stuff # You may use data setup in previous function as below: self.poll.get_absolute_url()
MyTest class may contain multiple tests for your app. All test functions start with “test_” prefix. setUpTestData is a classmethod that will run exactly once before all the tests. This method may create a dummy database entries for tests that you need to run next or setup variables and do other stuff according to your test environment needs. So all the setup part goes into this function. Database created by Django test command will be a dummy one ( different from your current deployment ) and will be destroyed once all the tests are completed.
Further details about Django test Client class and how to implement simple tests can be found at Django website. This is pretty much very easy to understand stuff and works right out of the box.
A bit in depth !!
In unit-testing you may face few parts of your app that may interact with other parts of your code and you don’t want them to do that at the time of unit-testing. A simple example will be : while testing my model save method my poll app uploads an image of that poll to my S3 image server. I don’t want this to happen during my tests. Another few cases may be about scripts writing to some external file or hitting cross site API for data that you may not want to happen during testing just the simple block of code. At this time python’s Mock library is the way to go.
Mock can simply replace a python object/function (as everything in python is an object) with its own Mock object. When you call this function than mock acts like that function and you don’t have to work with that particular function of your app and rather can concentrate your tests on your current unit-test block. Lets see this stuff in action:
from django.test import TestCase from mock import patch class PollModelTest(TestCase): """test for entity model""" @classmethod @patch('poll.models.file_add_data') @patch('poll.models.image_thumbnail') def setUpTestData(cls, mock_image_thumbnail, mock_file_handler): # when file_add_data or image_thumbnail functions are called here # they will be replaced by there mock objects
In this test class we want to test our Poll app models. But when we setup poll data the save method on poll model writes to a json file with “file_add_data” and also uploads a thumbnail image for that poll to our image s3 server. We don’t want this to happen when we are testing Poll models as we have already tested this functions in their unit-tests separately. So we use patch decorator of mock library. We replace this two functions in “poll.models” file as our save method on poll is written there ( this is one of the most important point to look at , that will keep you away from many rookie mistakes while using mock ). So this methods are now replaced by your new mock objects that are available as function arguments for you to check out!! You can call : assertEqual(mock_file_handler.call_count, 1) and verify that your save method indeed attempted to write json file once and it works fine. you may also use mock_image_thumbnail.mock_calls to get all the arguments that were passed to your function while poll save method called it. Mock object will also keep tabs of what methods were called on the object that you patched. With that you can easily unit-test your current module without worrying about other integration.
This is just the simplest case of use for mock. Read more about mock uses and you will see the power mock can provide your tests if used wisely.
As we have already seen that Django creates a whole new test database and destroys it after all tests have been completed. If you have multiple tests that dump a lot of data in your test database than your tests are gonna take a long long time to run due to all those database interactions. In test driven development you are going to write lot of tests and you gonna run it very often , so if they run slow than you will get the development time penalty.
To solve this problem some time it is best to use raw python models in your tests without actually saving them. I.e.
poll = Poll(name = "my new poll) # Do stuff with poll
Here you don’t call save method on the poll object and so it is not saved to the database. Django will always fetch it from the memory and that will improve a performance by a great factor.
Another neat and fast solution to this problem is Factory-boy. This python module needs a bit of setup and learning curve to get used to but it will be worth it in the end.
Automated front-end testing with selenium!!
selenium is a frontend testing module and now you can write selenium front-end tests together with your Django back-end tests. Selenium tests basically opens your browser ( now it also implements its own test selenium server you don’t need to keep a test server running ) hits the url you want to test and than asserts if required data is filled in your HTML or an element with certain ID is there or not. It can also fill in the user login form automatically and test your front-end validations very easily.
It is an amazing experience to just type “python manage.py test” and see your whole website being tested in front of your very eyes. Check out more details about selenium on its page.
This was basic over view of all the stuff you may need to know about or you may need if you want to implement amazing testing capabilities in your Django app. At the end of the day all depends on what goes with your heart and what do you like to use. In world of python there are so many choices we got. This are my few favorites.
I will add further posts on selenium testing in detail as that is a whole new topic in it self.
Hope this article will help you, Happy Coding !!