{"id":410714,"date":"2024-06-29T21:32:36","date_gmt":"2024-06-29T21:32:36","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=410714"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=410714","title":{"rendered":"<span>Stop losing clients! Or how a developer can test a website, by the example of PVS-Studio. Part 1<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>A website with bugs could be a real pain in the neck for business. Just one 404 or 500 error could end up costing an obscene amount of money for the company and hurt a good reputation. But there is a way to avoid this issue: the website testing. That&#8217;s sort of what this article is about. After reading this article, you will learn how to test code in Django, create your &#171;own website tester&#187;\u00a0and much more. Welcome to the article.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/4da\/858\/258\/4da85825868cd5de6984786818965c3d.jpg\" width=\"580\" height=\"327\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/4da\/858\/258\/4da85825868cd5de6984786818965c3d.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<h2>How do you feel when you are writing tests?<\/h2>\n<p>How would you answer this question? I would say that I&#8217;m enjoying writing them. Each developer has his own opinion about tests. Personally, I really love the process. The process of writing test helps me not only write more secure code, but also understand my own and other people&#8217;s programs better. And cherry on top is that feeling when all tests go green. At this point, my perfectionism scale reaches its peak.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/d31\/9e5\/3a3\/d319e53a3c49b7e65bbff14e0abd777b.jpg\" width=\"280\" height=\"441\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d31\/9e5\/3a3\/d319e53a3c49b7e65bbff14e0abd777b.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<p>Sometimes when testing, I get sucked into the process as if I&#8217;m playing Half-Life. I start to spend all my working time and free time on this process. Of course, over time, I get tired of tests, and then I have to take a break. After the break, I can become a no-lifer again for a few weeks, as if Valve released a new episode. If you&#8217;re the same as me, then you know what I&#8217;m talking about. Enough talk, let&#8217;s get down to business!<\/p>\n<h2>Backend testing<\/h2>\n<p>We build <a href=\"https:\/\/pvs-studio.com\/en\/\"><u>our website<\/u><\/a> on Django, so the code examples are for this framework.\u00a0<\/p>\n<p>Before starting, I invite you to read the list of recommendations that structure the process of writing tests and make it more comfortable. I made the list on the basis of my personal experience and other developers&#8217; tips.\u00a0<\/p>\n<ul>\n<li>\n<p>The test files are stored in the <em>tests<\/em> folder inside the app;\u00a0<\/p>\n<\/li>\n<li>\n<p>model tests, view tests and form tests are located in the <em>test_models.py<\/em>, <em>test_views.py<\/em> and <em>test_forms.py<\/em> respectively;\u00a0<\/p>\n<\/li>\n<\/ul>\n<ul>\n<li>\n<p>the test method name starts with the <em>test_<\/em> prefix (e.g, <em>test_get_sum<\/em> or <em>test_status_code<\/em>);\u00a0<\/p>\n<\/li>\n<li>\n<p>the name of the class that contains tests has the following form: <em>TestedEntityTests<\/em> (e.g, <em>TrialTests<\/em> or<em> FeedbackFileTests<\/em>).\u00a0<\/p>\n<\/li>\n<\/ul>\n<h3>Testing of models<\/h3>\n<p>Let&#8217;s create the <em>my_app<\/em> application and fill in the <a href=\"http:\/\/models.py\"><em>models.py<\/em><\/a> file with the following code:<\/p>\n<pre><code class=\"python\">from django.db import models   class Trial(models.Model):     \"\"\"Simple user trial model\"\"\"      email = models.EmailField(         verbose_name='Email',         max_length=256,         unique=False,     )      def __str__(self):         return str(self.email)      class Meta:         verbose_name = 'Trial'         verbose_name_plural = 'Trials'<\/code><\/pre>\n<p>This model is a simplified version of our <em>Trial<\/em> model. Here&#8217;s what we can check with this model:\u00a0<\/p>\n<ol>\n<li>\n<p>The <em>verbose_name<\/em> parameter of the <em>email<\/em> field \u2013 &#171;Email&#187;.\u00a0<\/p>\n<\/li>\n<li>\n<p>The <em>max_length<\/em> parameter of the <em>email<\/em> field \u2013 256.\u00a0<\/p>\n<\/li>\n<li>\n<p>The <em>unique<\/em> parameter of the <em>email<\/em> field \u2013 <em>False<\/em>.\u00a0<\/p>\n<\/li>\n<li>\n<p>The __<em>str__<\/em> method returns the <em>email<\/em> parameter value.\u00a0<\/p>\n<\/li>\n<li>\n<p>The <em>verbose_name<\/em> parameter of the model \u2013 &#171;Trial&#187;.\u00a0<\/p>\n<\/li>\n<li>\n<p>The <em>verbose_name_plural<\/em> parameter of the model \u2013 &#171;Trials&#187;.\u00a0<\/p>\n<\/li>\n<\/ol>\n<p>I have heard from some programmers that testing of models is a waste of time. But my experience suggests that this opinion is erroneous. Let me show you a simple example. For the <em>email<\/em> field, we set a maximum length of 256 characters (in accordance with <a href=\"https:\/\/www.ietf.org\/rfc\/rfc2821.txt\"><u>RFC 2821<\/u><\/a>). Accidentally deleting the last digit is not a big deal.\u00a0 If such an oversight suddenly happens, the user with the my_super_long_email@gmail.com (29 characters) email will get an error and won&#8217;t be able to request a trial. This means that the company will lose a prospective client. Of course, you can write additional validation, but it&#8217;s better to be sure that the program works successfully without it.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/10c\/fa2\/b86\/10cfa2b867f842ace1af1ec1b9605e6b.png\" width=\"476\" height=\"452\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/10c\/fa2\/b86\/10cfa2b867f842ace1af1ec1b9605e6b.png\"\/><figcaption><\/figcaption><\/figure>\n<p>Let&#8217;s move on to the tests and first decide where they will be located. You can write all the tests in one file \u2014 <em>tests.py <\/em>(Django adds this file when you create the application). Or you can follow the recommendations above and sort them.\u00a0<\/p>\n<p>If you like the second option more, delete <em>tests.py<\/em>. Then create the <em>tests<\/em> folder with the empty __<em>init.py__<\/em> file. When running the tests, the file will tell Python where to look for tests. Let&#8217;s add 3 more files to the same folder: <em>test_forms.py<\/em>, <em>test_models.py<\/em>, and <em>test_views.py<\/em>. The content of the application directory will be something like this:\u00a0<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/8f2\/d4f\/bc7\/8f2d4fbc7faef39969e6a7c2bd33e1c7.jpg\" width=\"175\" height=\"289\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/8f2\/d4f\/bc7\/8f2d4fbc7faef39969e6a7c2bd33e1c7.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<p>Let&#8217;s open the <em>test_models.py<\/em> file and add the following code to it:\u00a0<\/p>\n<pre><code class=\"python\">from django.test import TestCase  from my_app.models import Trial   class TrialTests(TestCase):     \"\"\"Tests for Trial model\"\"\"      def test_verbose_name(self):         pass      def test_max_length(self):         pass      def test_unique(self):         pass      def test_str_method(self):         pass      def test_model_verbose_name(self):         pass      def test_model_verbose_name_plural(self):         pass<\/code><\/pre>\n<p>Django has a special <em>django.test<\/em> module for testing. One of the most important classes of this model is <a href=\"https:\/\/docs.djangoproject.com\/en\/4.0\/topics\/testing\/tools\/#django.test.TestCase\"><em><u>TestCase<\/u><\/em><\/a>. It is the class that allows you to write tests. To write tests, we just need to inherit our class from <em>TestCase.<\/em><\/p>\n<p>All our tests are methods of the <em>TrialTests<\/em> class. The tests don&#8217;t check anything yet, but it won&#8217;t be for long. Each of the methods will test one condition from the list above. Let&#8217;s figure out how to run the tests. To run all tests of your website at once, enter this command in the console:<\/p>\n<pre><code class=\"bash\">python manage.py test<\/code><\/pre>\n<p>To run tests of a specific class, for example, <em>TrialTests<\/em>, write:<\/p>\n<pre><code class=\"bash\">python manage.py test my_app.tests.test_models.TrialTests<\/code><\/pre>\n<p>Any of these commands will run our 6 tests. Select one of them, enter it into the console, press Enter. We will get something like this:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/7e0\/5cb\/6e5\/7e05cb6e5d31e7925bb1c7b4be1e0a39.jpg\" width=\"388\" height=\"127\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/7e0\/5cb\/6e5\/7e05cb6e5d31e7925bb1c7b4be1e0a39.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<p>The output shows that 6 tests were checked in 0.001 seconds. &#171;OK&#187; at the end of the output indicates their successful execution.\u202f\u00a0<\/p>\n<p>Now let&#8217;s write real tests. To write them, we need to access the parameters of the <em>Trial<\/em> model object. So, we need to create this object. And here, it&#8217;s important to know that Django uses a separate clean database for tests. Before running the tests, the database is created. After running the tests, the database is deleted. That&#8217;s what are the first and last lines about in the screenshot above. If suddenly, for some reason, the base could not be deleted, Django tells you about that issue. You need to delete it manually.\u00a0<\/p>\n<p>To work with this database, you can use 3 methods:<\/p>\n<ol>\n<li>\n<p><em>setUp<\/em> \u2014 is executed before running each test;\u00a0<\/p>\n<\/li>\n<li>\n<p><em>tearDown<\/em> \u2014 is executed after completion of each test;\u00a0<\/p>\n<\/li>\n<li>\n<p><em>setUpTestData<\/em> \u2014 is executed before running all tests of a particular class.\u00a0<\/p>\n<\/li>\n<\/ol>\n<p>Let&#8217;s use the <a href=\"https:\/\/docs.djangoproject.com\/en\/4.0\/topics\/testing\/tools\/#django.test.TestCase.setUpTestData\"><u>latter<\/u><\/a>. Since it is a method of the class, let&#8217;s add the appropriate decorator. Inside, we create an object of the <em>Trial<\/em> class and get the <em>email<\/em> field from it. We will use the field in the tests.<\/p>\n<pre><code class=\"python\">class TrialTests(TestCase):     \"\"\"Tests for Trial model\"\"\"      @classmethod     def setUpTestData(cls):         \"\"\"Set up the database before running tests of the class\"\"\"          cls.trial = Trial.objects.create(             email='test@gmail.com'         )         cls.email_field = cls.trial._meta.get_field('email')<\/code><\/pre>\n<p>Now, when running tests of the <em>TrialTests<\/em> class, a <em>trial<\/em> object is created in the new database. After the run, the object is deleted.\u00a0<\/p>\n<p>Let&#8217;s write the test of the <em>verbose_name<\/em> parameter.\u00a0<\/p>\n<pre><code class=\"python\">def test_verbose_name(self):     \"\"\"The verbose_name parameter test\"\"\"      real_verbose_name = getattr(self.email_field, 'verbose_name')     expected_verbose_name = 'Email'      self.assertEqual(real_verbose_name, expected_verbose_name)<\/code><\/pre>\n<p>From the <em>email_field<\/em> field we extract the value of the <em>verbose_name<\/em> parameter. Then we apply the <em>assertEqual<\/em> method from the <em>TestCase<\/em> class. The method compares two parameters &#8212; the real and expected values of <em>verbose_name. <\/em>If the values are equal, the test runs successfully. Otherwise, it fails.\u00a0<\/p>\n<p>Let&#8217;s write the same tests for the <em>max_length<\/em> and <em>unique<\/em> parameters.\u00a0<\/p>\n<pre><code class=\"python\">def test_max_length(self):     \"\"\"The max_length parameter test\"\"\"      real_max_length = getattr(self.email_field, 'max_length')      self.assertEqual(real_max_length, 256)  def test_unique(self):     \"\"\"The unique parameter test\"\"\"      real_unique = getattr(self.email_field, 'unique')      self.assertEqual(real_unique, False)<\/code><\/pre>\n<p>It&#8217;s the same as with <em>verbose_name<\/em>.\u00a0<\/p>\n<p>By the way, in the <em>unique<\/em> parameter test, we check that the value is <em>False<\/em>. The <em>assertFalse<\/em> command makes it easier to do. Let&#8217;s rewrite the code of this test.\u00a0<\/p>\n<pre><code class=\"python\">def test_unique(self):     \"\"\"The unique parameter test\"\"\"      real_unique = getattr(self.email_field, 'unique')      self.assertFalse(real_unique)<\/code><\/pre>\n<p>The code is shorter and more readable. By the way, Django has many such <a href=\"https:\/\/docs.djangoproject.com\/en\/3.2\/topics\/testing\/tools\/#assertions\"><u>helpful assertions<\/u><\/a>.\u00a0<\/p>\n<p>Now let&#8217;s check the string representation of the object.\u00a0<\/p>\n<pre><code class=\"python\">def test_string_representation(self):     \"\"\"The __str__ method test\"\"\"      self.assertEqual(str(self.trial), str(self.trial.email))<\/code><\/pre>\n<p>That one&#8217;s easy. We check that the string representation of the object equals its email.\u00a0<\/p>\n<p>And the last thing is the tests of the model fields:\u00a0<\/p>\n<pre><code class=\"python\">def test_model_verbose_name(self):     \"\"\"The test of the verbose_name field of the Trial model\"\"\"      self.assertEqual(Trial._meta.verbose_name, 'Trial')  def test_model_verbose_name_plural(self):     \"\"\"The test of the verbose_name_plural fields of the Trial model\"\"\"      self.assertEqual(Trial._meta.verbose_name_plural, 'Trials')<\/code><\/pre>\n<p>Let&#8217;s access the fields of the <em>Trial<\/em> model through <em>_meta<\/em> and compare their value with the expected one.\u00a0<\/p>\n<p>If you run the tests now, they will run successfully, as before. Well, that&#8217;s no fun! Let&#8217;s break something. Let the <em>verbose_name<\/em> parameter of the <em>Trial<\/em> model become our victim. Open the model&#8217;s code and change the value of this field from &#171;Trial&#187; to &#171;Something else&#187;. Let&#8217;s run the tests.\u00a0<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/472\/2a5\/428\/4722a5428fed8d439feabf7eae1fb8d0.png\" width=\"474\" height=\"264\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/472\/2a5\/428\/4722a5428fed8d439feabf7eae1fb8d0.png\"\/><figcaption><\/figcaption><\/figure>\n<p>As you can see, one of the tests failed. Django tells us about the failure and that the real value of the field (&#171;Something else&#187;) doesn&#8217;t equal the expected value (&#171;Trial&#187;).\u00a0<\/p>\n<h3>Mixins &#8212; the helpful guys<\/h3>\n<p>Model tests are homogeneous. So, when you have a lot of entities, testing them is not the most pleasant routine. I tried to simplify this process somewhat with mixins. My method is not perfect, and I do not insist on using it. However, you may find it useful.\u00a0<\/p>\n<p>I think you noticed that when we test the <em>verbose_name<\/em>, <em>max_length<\/em>, and <em>unique<\/em> fields, we see some code duplication. We get the value of the object field and compare it with the expected one. And so it&#8217;s in all three tests. That means, you can write one function that does all the work.\u00a0<\/p>\n<pre><code class=\"python\">def run_field_parameter_test(         model, self_,         field_and_parameter_value: dict,         parameter_name: str) -> None:     \"\"\"Test field\u2019s parameter value\"\"\"      for instance in model.objects.all():         # Example 1: field = \"email\"; expected_value = 256.         # Example 2: field = \"email\"; expected_value = \"Email\".         for field, expected_value in field_and_parameter_value.items():             parameter_real_value = getattr(                 instance._meta.get_field(field), parameter_name             )              self_.assertEqual(parameter_real_value, expected_value)<\/code><\/pre>\n<p>Let&#8217;s figure out what parameters we use. I think it&#8217;s clear why we use <em>model<\/em>. Then we use <em>self_<\/em> and we need it only to call the <em>assertEqual<\/em> method. Since <em>self<\/em> is a keyword in Python, we add _ to avoid misunderstandings. <em>field_and_parameter_value<\/em> is a dictionary with a field and the value of the field&#8217;s parameter. For example, if we check the <em>max_length<\/em> parameter, we can pass <em>email<\/em> and 256 to this variable. If we check <em>verbose_name<\/em>, then we pass <em>email<\/em> and &#171;Email&#187;. <em>parameter_name<\/em> is the parameter being tested: <em>max_length<\/em>, <em>verbose_name<\/em> etc.\u00a0<\/p>\n<p>Now let&#8217;s turn to the code. First, we get all the objects of the model and go through them. Next, we go through the dictionary that contains fields and expected parameter values. After that, we get the real parameter values by referring to the object. And then we compare them with the expected values. The code is very similar to the one previously written in tests. Only now it&#8217;s all in one function. By the way, if the function name had started with the <em>test_<\/em> prefix, Django would have considered this function the real test and would have tried to run it along with the others.\u00a0<\/p>\n<p>Let&#8217;s write mixins. Each field should have its own mixin. For example, let&#8217;s take the <em>verbose_name<\/em> and <em>max_length<\/em> fields.\u00a0<\/p>\n<pre><code class=\"python\">class TestVerboseNameMixin:     \"\"\"Mixin to check verbose_name\"\"\"      def run_verbose_name_test(self, model):         \"\"\"Function that tests verbose_name\"\"\"          run_field_parameter_test(             model, self, self.field_and_verbose_name, 'verbose_name'         )   class TestMaxLengthMixin:     \"\"\"Mixin to check max_length\"\"\"      def run_max_length_test(self, model):         \"\"\"Function that tests max_length\"\"\"          run_field_parameter_test(             model, self, self.field_and_max_length, 'max_length'         )<\/code><\/pre>\n<p>We create the necessary method. In this method, we call our single function with the corresponding parameters. <em>self.field_and_verbose_name<\/em> and <em>self.field_and_max_length <\/em>are taken from the class inherited from the mixin. Namely, it is taken from the <em>setUpTestData<\/em> method of the <em>TrialTests<\/em> class.<\/p>\n<pre><code class=\"python\">@classmethod  def setUpTestData(cls):      # ...     cls.field_and_verbose_name = {         'email': 'Email',     }      cls.field_and_max_length = {         'email': 256,     }<\/code><\/pre>\n<p>Let&#8217;s inherit the <em>TrialTests<\/em> class from our mixins.<\/p>\n<pre><code class=\"python\">class TrialTests(TestCase, TestVerboseNameMixin, TestMaxLengthMixin):     # ...<\/code><\/pre>\n<p>If you have a lot of mixins, you can combine them. For example, combine them into a tuple and unpack it when inheriting.<\/p>\n<pre><code class=\"python\">MIXINS_SET = (     TestVerboseNameMixin, TestMaxLengthMixin, )   class TrialTests(TestCase, *MIXINS_SET):     # ...<\/code><\/pre>\n<p>Now we can rewrite our tests:\u00a0<\/p>\n<pre><code class=\"python\">def test_verbose_name(self):     \"\"\"The verbose_name parameter test\"\"\"      super().run_verbose_name_test(Trial)  def test_max_length(self):     \"\"\"The max_length parameter test\"\"\"      super().run_max_length_test(Trial) <\/code><\/pre>\n<p>When you have a lot of tests for different models, this method turns out to be very useful.\u00a0<\/p>\n<h3>Testing logic<\/h3>\n<p>Let&#8217;s test the code from the <em>views.py<\/em> file. For example, let&#8217;s take the function that gets a domain from an email.<\/p>\n<pre><code class=\"python\">def get_domain(email: str) -> str:     \"\"\"Return email's domain\"\"\"      try:         _, domain = email.split('@')     except ValueError:         domain = ''      return domain<\/code><\/pre>\n<p>This is what the test of the function might look like:<\/p>\n<pre><code class=\"python\">from django.test import TestCase  from my_app.views import get_domain   EMAIL_AND_DOMAIN = {     'test1@gmail.com': 'gmail.com',     'test2@wrong_email': 'wrong_email',     'test3@mail.ru': 'mail.ru',     'test4@@wrong_email.com': '', }   class FunctionsTests(TestCase):     \"\"\"Test class for views\"\"\"      def test_get_domain(self):         \"\"\"Test get_domain function\"\"\"          for email, expected_domain in EMAIL_AND_DOMAIN.items():             real_domain = get_domain(email)              self.assertEqual(real_domain, expected_domain)<\/code><\/pre>\n<p>The constant stores emails and their real domains. In the test, we go through the emails. With the help of the function under test, we get the domain and compare it with the expected one.\u00a0<\/p>\n<p>Now let&#8217;s talk a little about one useful construction. Let me change our emails somehow. For example, let&#8217;s change <em>test1@gmail.com<\/em> to<em> test1@habr.com<\/em> , and<em> test2@wrong_email<\/em> to<em> test2@habr<\/em>. Time to run the tests.\u00a0<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/e3b\/b8b\/f46\/e3bb8bf468324784c7d827fe00eaa855.jpg\" width=\"580\" height=\"323\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e3b\/b8b\/f46\/e3bb8bf468324784c7d827fe00eaa855.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<p>They failed as expected. But why do we see that only one email is incorrect, even though we changed two? You see, by default, Django doesn&#8217;t continue testing if a failure occurs. Django simply stops to run tests, as if the command <em>break<\/em> is called inside the loop. This fact can hardly please you, especially if your tests could take forever. But, luckily, there is a solution \u2014 the <a href=\"https:\/\/docs.python.org\/3\/library\/unittest.html#unittest.TestCase.subTest\"><em><u>with self.subTest()<\/u><\/em><\/a> construction. The construction is specified after the loop declaration. Let&#8217;s add it to our test:<\/p>\n<pre><code class=\"python\"># ... for email, expected_doamin in EMAIL_AND_DOMAIN.items():     with self.subTest(f'{email=}'):         real_domain = get_domain(email)          self.assertEqual(real_domain, expected_doamin)<\/code><\/pre>\n<p>In the brackets of the <em>subTest<\/em> method, we specify the line that we want to output when the test fails. In our case, this is the email being tested.\u00a0<\/p>\n<p>Now, if any test fails, Django will save a report about the failure and continue running. And after the run is completed, Django will display information on every test that failed.\u00a0<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/22e\/f5f\/8e6\/22ef5f8e64f735e8a58269f24f59e267.jpg\" width=\"589\" height=\"582\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/22e\/f5f\/8e6\/22ef5f8e64f735e8a58269f24f59e267.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<p>Let&#8217;s look at the test of another function. When we get a promo code from a user, we transform it into a more convenient form \u2013 we remove the &#171;#&#187; characters and spaces. To do this, we have the <em>get_correct_promo<\/em> function:<\/p>\n<pre><code class=\"python\">def get_correct_promo(promo: str) -> str:     \"\"\"Get promo without # and whitespaces\"\"\"      return promo.replace('#', '').replace(' ', '')<\/code><\/pre>\n<p>This is what the function test might look like:<\/p>\n<pre><code class=\"python\">from django.test import TestCase  from my_app.views import get_correct_promo   PROMO_CODES = {     '#sast': 'sast',     '#beauty#': 'beauty',     '#test test2': 'testtest2',     'test1 test2 test3': 'test1test2test3', }   class FunctionsTests(TestCase):     \"\"\"Test class for views\"\"\"      def test_get_correct_promo(self):         \"\"\"Test get_correct_promo function\"\"\"          for incorrect_promo, correct_promo in PROMO_CODES.items():             real_promo = get_correct_promo(incorrect_promo)              self.assertEqual(real_promo, correct_promo)<\/code><\/pre>\n<p>The constant stores incorrect and correct promo codes. In the test, we go through the promo codes. After that, we compare the promo code obtained with the <em>get_correct_promo<\/em> function and the correct promo code.\u00a0<\/p>\n<p>Probably, views testing is the simplest of this triad of tests. In this kind of testing, we simply call the function we need. Then we check that the value the function returns matches the expected one. By the way, when creating constants with data for testing, I recommend that you come up with many different values as possible. This way you will increase the chances that your test will become more effective.\u00a0<\/p>\n<h3>Form tests<\/h3>\n<p>Form tests are similar to model tests. In form tests, we can also check fields and methods.\u00a0<\/p>\n<p>Let&#8217;s create a <em>Trial<\/em> model form:\u00a0<\/p>\n<pre><code class=\"python\">from django import forms  from my_app.models import Trial   class TrialForm(forms.ModelForm):     \"\"\"Form of Trial model\"\"\"      class Meta:         model = Trial         exclude = ()<\/code><\/pre>\n<p>This is what the function test might look like:<\/p>\n<pre><code class=\"python\">from django.test import TestCase  from my_app.forms import TrialForm   class TrialFormTests(TestCase):     \"\"\"Tests for TrialForm form\"\"\"      def test_field_labels(self):         \"\"\"Test field's labels\"\"\"          form = TrialForm()         email_label = form.fields['email'].label          self.assertEqual(email_label, 'Email')<\/code><\/pre>\n<p>In the test, we create an object of our form and compare the <em>label<\/em> of the field with the expected one. This is how you can write form tests. But we hardly use them. There is a more effective way to test forms. That&#8217;s sort of what the second part of the article is about.<\/p>\n<h2>How to create your &#171;own website tester&#187;<\/h2>\n<p>So, you tested the backend of your website. But suddenly, you noticed the 404 error on one of the pages. The tests you wrote did not find this error. These tests also won&#8217;t help, for example, when searching for dead links on pages. Such tests are simply not designed for bugs of this kind. But then how to catch these bugs? In this case, we need tests that simulate user actions. You can use <a href=\"https:\/\/docs.djangoproject.com\/en\/4.0\/topics\/testing\/tools\/#the-test-client\"><em><u>django.test.Client<\/u><\/em><\/a>, but it allows you to run tests only on the website server itself. It&#8217;s not always convenient. So, let&#8217;s turn to the Python <em>requests<\/em> library.\u00a0<\/p>\n<p>The tests usually turn out to be voluminous. It&#8217;s better to put them in a separate file (or files), for example \u2014 <em>test_requests.py<\/em>.\u00a0<\/p>\n<h3>Checking status codes<\/h3>\n<p>To check the page status code, you need:\u00a0<\/p>\n<ol>\n<li>\n<p>Go to the website page;<\/p>\n<\/li>\n<li>\n<p>Get the status code of the website page;<\/p>\n<\/li>\n<li>\n<p>Check that the status code is 200.<\/p>\n<\/li>\n<\/ol>\n<p>The <em>requests<\/em> library has many useful <a href=\"https:\/\/docs.python-requests.org\/en\/stable\/api\/\">methods<\/a>. The <em>head<\/em> method will help us to do the 1st and the 2nd list points. We will use the method to send the HEAD request to the website pages. Let&#8217;s import this method.<\/p>\n<pre><code class=\"python\">from requests import head<\/code><\/pre>\n<p>We only need to pass the URL to the method to get a response with all the necessary information about the page. And from this information, you can extract the status code:<\/p>\n<pre><code class=\"python\">response = head('&lt;page url>') print(response.status_code)<\/code><\/pre>\n<p>Now let&#8217;s move on to test writing. Create the necessary constants: the website domain and the relative paths of the website pages. For simplicity, let&#8217;s take the domain of only the English website version.<\/p>\n<pre><code class=\"python\">DOMAIN = 'https:\/\/pvs-studio.com\/en\/'  PAGES = (     '',     'address\/',     'pvs-studio\/',     'pvs-studio\/download\/',     # ... )  PAGES = (DOMAIN + page for page in PAGES)<\/code><\/pre>\n<p>Of course, ideally, it is better to take the relative paths of pages from the database. But if there is no such possibility \u2014 you can use a tuple.\u00a0\u00a0<\/p>\n<p>Let&#8217;s add the PagesTests class together with the test_status_code test:\u00a0<\/p>\n<pre><code class=\"python\">from django.test import TestCase   class PagesTests(TestCase):     \"\"\"Tests for pages\"\"\"      def test_status_code(self):         \"\"\"Test status code for pages\"\"\"          for page in PAGES:             with self.subTest(f'{page=}'):                 response = head(page) # (1)                  self.assertEqual(response.status_code, 200) # (2) \u0438 (3)<\/code><\/pre>\n<p>In the test, we send the HEAD request to each page and save the response. After that, we check whether the page status code is equal to 200.<\/p>\n<h3>Checking links on pages<\/h3>\n<p>Here&#8217;s a way how to check a link:<\/p>\n<ol>\n<li>\n<p>Send the GET request to the page and get the page content;<\/p>\n<\/li>\n<li>\n<p>Use a regular expression to get all the links from the content;<\/p>\n<\/li>\n<li>\n<p>Go through each link and check that the link status code is 200.<\/p>\n<\/li>\n<\/ol>\n<p>To search for links, let&#8217;s use the <em>findall<\/em> method of the <em>re<\/em> module. To send a GET request, let&#8217;s use the <em>get<\/em> method of the same <em>requests<\/em> library. And remember about the <em>head<\/em> method.<\/p>\n<pre><code class=\"python\">from re import findall  from requests import get, head<\/code><\/pre>\n<p>Next, let&#8217;s move on to the variables. For this test, we need the <em>PAGES<\/em> constant declared earlier,\u00a0and the variable with a regular expression for the link.<\/p>\n<pre><code class=\"python\">LINK_REGULAR_EXPRESSION = r'&lt;a[^>]* href=\"([^\"]*)\"'<\/code><\/pre>\n<p>\u0418, \u043d\u0430\u043a\u043e\u043d\u0435\u0446, \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0441\u0430\u043c \u0442\u0435\u0441\u0442.<\/p>\n<pre><code class=\"python\">def test_links(self):     \"\"\"Test links on all site pages\"\"\"      valid_links = set()      for page in PAGES:         page_content = get(page).content # (1)         page_links = set( # (2)             findall(LINK_REGULAR_EXPRESSION, str(page_content))         )          for link in page_links:             if link in valid_links:                 continue              with self.subTest(f'{link=} | {page=}'):                 response = head(link, allow_redirects=True)                  if response.status_code == 200:                     valid_links.add(link)                  self.assertEqual(response.status_code, 200) # (3)<\/code><\/pre>\n<p>We send the GET request to each page and extract content from the received response. Next, we use the regular expression and the <em>findall<\/em> method and get all the links located on the page. We put these links to the set to remove duplicates. The last stage is a familiar scenario: we go through all the links, send the HEAD request to these links, and check the status code. If the link variable is a redirect, the <a href=\"https:\/\/docs.python-requests.org\/en\/v0.8.4\/api\/#requests.Request.allow_redirects\"><em><u>allow_redirects<\/u><\/em><\/a> parameter will indicate whether we can execute the redirect. By default, its value is False. We also add valid links to set in order not to send a request to them in the future.\u00a0<\/p>\n<p>By the way, sometimes you can find relative links on the page. For example, &#171;\/ru\/pvs-studio\/faq\/&#187;. The website adds the URL to these links, while the test does not do this. As a result, the test cannot handle the request.\u00a0<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/5c9\/58a\/29e\/5c958a29eb3966e4d4225327ce28e867.jpg\" width=\"553\" height=\"35\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5c9\/58a\/29e\/5c958a29eb3966e4d4225327ce28e867.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<p>To avoid this issue, let&#8217;s create a function:<\/p>\n<pre><code class=\"python\">SITE_URL = 'https:\/\/pvs-studio.com'  def get_full_link(link: str) -> str:     \"\"\"Return link with site\u2019s url\"\"\"      if not link.startswith('http'):         link = SITE_URL + link      return link<\/code><\/pre>\n<p>If the received link is relative, the function adds the URL of the website to this link. Now in the test, when we receive the link, we will use the following function:<\/p>\n<pre><code class=\"python\"># ... for link in page_links:     link = get_full_link(link) # ...<\/code><\/pre>\n<p>There are situations when the test does not show the real status code of the page. It is usually either 403 or 404. For example, for <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=rvo.SendEmailTask&amp;amp\"><u>this page<\/u><\/a>, <em>head<\/em> will return the 404 status code. This happens because some websites don\u2019t want to give page data to robots. To avoid this, you need to use the <em>get<\/em> method, and for greater confidence in the test, add a header with the <em>User-Agent<\/em>.<\/p>\n<pre><code class=\"python\">from requests import get  head_response = head(link) print(head_response.status_code) # 404  get_response = get(link, headers={'User-Agent': 'Mozilla\/5.0'}) print(get_response.status_code) # 200<\/code><\/pre>\n<h3>Redirect Tests<\/h3>\n<p>Another variant of tests where you can use <em>requests<\/em> is redirect tests.\u00a0 To check redirect tests, we need to:<\/p>\n<ol>\n<li>\n<p>Follow the link and get the response;<\/p>\n<\/li>\n<li>\n<p>Compare the response URL with the expected one.<\/p>\n<\/li>\n<\/ol>\n<p>So, we need two URLs. The first URL is a redirect link that the user clicks on. The second one is the URL of the page that the visitor eventually went to. As in the example with status codes, it&#8217;s better to get these URLs from the database. If this is not possible, then I recommend using a dictionary.<\/p>\n<pre><code class=\"python\">REDIRECT_URLS = {     '\/ru\/m\/0008\/': '\/ru\/docs\/',     '\/en\/articles\/': '\/en\/blog\/posts\/',     '\/ru\/d\/full\/': '\/ru\/docs\/manual\/full\/', }<\/code><\/pre>\n<p>Let&#8217;s remember about the <em>SITE_URL<\/em> variable created earlier.<\/p>\n<pre><code class=\"python\">SITE_URL = 'https:\/\/pvs-studio.com'<\/code><\/pre>\n<p>Now, let&#8217;s write the test.<\/p>\n<pre><code class=\"python\">def test_redirects(self):     \"\"\"Test the correctness of the redirect\"\"\"      for link, page_url in REDIRECT_URLS.items():         with self.subTest(f'{link=} | {page_url=}'):             page_response = head(                 SITE_URL + link, allow_redirects=True             ) # (1)              expected_page_url = SITE_URL + page_url              self.assertEqual(page_response.url, expected_page_url) # (2)<\/code><\/pre>\n<p>First, we send the HEAD request using the link. At the same time, we allow the usage of redirects. From the received response, we take the URL of the page and compare it with the expected one.\u00a0<\/p>\n<p>The <em>requests<\/em> library allows you to perform many different website tests. The main methods for tests, as you may have noticed are <em>head<\/em> and <em>get<\/em>. But there are <a href=\"https:\/\/docs.python-requests.org\/en\/latest\/api\/\"><u>other methods<\/u><\/a>. And they can also be useful. It all depends on your tasks.\u00a0<\/p>\n<h2>Conclusion<\/h2>\n<p>So, now you know how to write tests for the backend and how to create your &#171;own website tester&#187;. We will talk about form testing, JS, page translation testing and so on in the\u00a0next parts of the article. Do you have any comments or feedback? Write down them bellow or to <a href=\"https:\/\/www.instagram.com\/stepanov.programmer\/\"><u>my instagram<\/u><\/a>. Thank you for reading this article, and see you soon!)<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/649189\/\"> https:\/\/habr.com\/ru\/articles\/649189\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>A website with bugs could be a real pain in the neck for business. Just one 404 or 500 error could end up costing an obscene amount of money for the company and hurt a good reputation. But there is a way to avoid this issue: the website testing. That&#8217;s sort of what this article is about. After reading this article, you will learn how to test code in Django, create your &#171;own website tester&#187;\u00a0and much more. Welcome to the article.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<h2>How do you feel when you are writing tests?<\/h2>\n<p>How would you answer this question? I would say that I&#8217;m enjoying writing them. Each developer has his own opinion about tests. Personally, I really love the process. The process of writing test helps me not only write more secure code, but also understand my own and other people&#8217;s programs better. And cherry on top is that feeling when all tests go green. At this point, my perfectionism scale reaches its peak.<\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<p>Sometimes when testing, I get sucked into the process as if I&#8217;m playing Half-Life. I start to spend all my working time and free time on this process. Of course, over time, I get tired of tests, and then I have to take a break. After the break, I can become a no-lifer again for a few weeks, as if Valve released a new episode. If you&#8217;re the same as me, then you know what I&#8217;m talking about. Enough talk, let&#8217;s get down to business!<\/p>\n<h2>Backend testing<\/h2>\n<p>We build <a href=\"https:\/\/pvs-studio.com\/en\/\"><u>our website<\/u><\/a> on Django, so the code examples are for this framework.\u00a0<\/p>\n<p>Before starting, I invite you to read the list of recommendations that structure the process of writing tests and make it more comfortable. I made the list on the basis of my personal experience and other developers&#8217; tips.\u00a0<\/p>\n<ul>\n<li>\n<p>The test files are stored in the <em>tests<\/em> folder inside the app;\u00a0<\/p>\n<\/li>\n<li>\n<p>model tests, view tests and form tests are located in the <em>test_models.py<\/em>, <em>test_views.py<\/em> and <em>test_forms.py<\/em> respectively;\u00a0<\/p>\n<\/li>\n<\/ul>\n<ul>\n<li>\n<p>the test method name starts with the <em>test_<\/em> prefix (e.g, <em>test_get_sum<\/em> or <em>test_status_code<\/em>);\u00a0<\/p>\n<\/li>\n<li>\n<p>the name of the class that contains tests has the following form: <em>TestedEntityTests<\/em> (e.g, <em>TrialTests<\/em> or<em> FeedbackFileTests<\/em>).\u00a0<\/p>\n<\/li>\n<\/ul>\n<h3>Testing of models<\/h3>\n<p>Let&#8217;s create the <em>my_app<\/em> application and fill in the <a href=\"http:\/\/models.py\"><em>models.py<\/em><\/a> file with the following code:<\/p>\n<pre><code class=\"python\">from django.db import models   class Trial(models.Model):     \"\"\"Simple user trial model\"\"\"      email = models.EmailField(         verbose_name='Email',         max_length=256,         unique=False,     )      def __str__(self):         return str(self.email)      class Meta:         verbose_name = 'Trial'         verbose_name_plural = 'Trials'<\/code><\/pre>\n<p>This model is a simplified version of our <em>Trial<\/em> model. Here&#8217;s what we can check with this model:\u00a0<\/p>\n<ol>\n<li>\n<p>The <em>verbose_name<\/em> parameter of the <em>email<\/em> field \u2013 &#171;Email&#187;.\u00a0<\/p>\n<\/li>\n<li>\n<p>The <em>max_length<\/em> parameter of the <em>email<\/em> field \u2013 256.\u00a0<\/p>\n<\/li>\n<li>\n<p>The <em>unique<\/em> parameter of the <em>email<\/em> field \u2013 <em>False<\/em>.\u00a0<\/p>\n<\/li>\n<li>\n<p>The __<em>str__<\/em> method returns the <em>email<\/em> parameter value.\u00a0<\/p>\n<\/li>\n<li>\n<p>The <em>verbose_name<\/em> parameter of the model \u2013 &#171;Trial&#187;.\u00a0<\/p>\n<\/li>\n<li>\n<p>The <em>verbose_name_plural<\/em> parameter of the model \u2013 &#171;Trials&#187;.\u00a0<\/p>\n<\/li>\n<\/ol>\n<p>I have heard from some programmers that testing of models is a waste of time. But my experience suggests that this opinion is erroneous. Let me show you a simple example. For the <em>email<\/em> field, we set a maximum length of 256 characters (in accordance with <a href=\"https:\/\/www.ietf.org\/rfc\/rfc2821.txt\"><u>RFC 2821<\/u><\/a>). Accidentally deleting the last digit is not a big deal.\u00a0 If such an oversight suddenly happens, the user with the my_super_long_email@gmail.com (29 characters) email will get an error and won&#8217;t be able to request a trial. This means that the company will lose a prospective client. Of course, you can write additional validation, but it&#8217;s better to be sure that the program works successfully without it.<\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<p>Let&#8217;s move on to the tests and first decide where they will be located. You can write all the tests in one file \u2014 <em>tests.py <\/em>(Django adds this file when you create the application). Or you can follow the recommendations above and sort them.\u00a0<\/p>\n<p>If you like the second option more, delete <em>tests.py<\/em>. Then create the <em>tests<\/em> folder with the empty __<em>init.py__<\/em> file. When running the tests, the file will tell Python where to look for tests. Let&#8217;s add 3 more files to the same folder: <em>test_forms.py<\/em>, <em>test_models.py<\/em>, and <em>test_views.py<\/em>. The content of the application directory will be something like this:\u00a0<\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<p>Let&#8217;s open the <em>test_models.py<\/em> file and add the following code to it:\u00a0<\/p>\n<pre><code class=\"python\">from django.test import TestCase  from my_app.models import Trial   class TrialTests(TestCase):     \"\"\"Tests for Trial model\"\"\"      def test_verbose_name(self):         pass      def test_max_length(self):         pass      def test_unique(self):         pass      def test_str_method(self):         pass      def test_model_verbose_name(self):         pass      def test_model_verbose_name_plural(self):         pass<\/code><\/pre>\n<p>Django has a special <em>django.test<\/em> module for testing. One of the most important classes of this model is <a href=\"https:\/\/docs.djangoproject.com\/en\/4.0\/topics\/testing\/tools\/#django.test.TestCase\"><em><u>TestCase<\/u><\/em><\/a>. It is the class that allows you to write tests. To write tests, we just need to inherit our class from <em>TestCase.<\/em><\/p>\n<p>All our tests are methods of the <em>TrialTests<\/em> class. The tests don&#8217;t check anything yet, but it won&#8217;t be for long. Each of the methods will test one condition from the list above. Let&#8217;s figure out how to run the tests. To run all tests of your website at once, enter this command in the console:<\/p>\n<pre><code class=\"bash\">python manage.py test<\/code><\/pre>\n<p>To run tests of a specific class, for example, <em>TrialTests<\/em>, write:<\/p>\n<pre><code class=\"bash\">python manage.py test my_app.tests.test_models.TrialTests<\/code><\/pre>\n<p>Any of these commands will run our 6 tests. Select one of them, enter it into the console, press Enter. We will get something like this:<\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<p>The output shows that 6 tests were checked in 0.001 seconds. &#171;OK&#187; at the end of the output indicates their successful execution.\u202f\u00a0<\/p>\n<p>Now let&#8217;s write real tests. To write them, we need to access the parameters of the <em>Trial<\/em> model object. So, we need to create this object. And here, it&#8217;s important to know that Django uses a separate clean database for tests. Before running the tests, the database is created. After running the tests, the database is deleted. That&#8217;s what are the first and last lines about in the screenshot above. If suddenly, for some reason, the base could not be deleted, Django tells you about that issue. You need to delete it manually.\u00a0<\/p>\n<p>To work with this database, you can use 3 methods:<\/p>\n<ol>\n<li>\n<p><em>setUp<\/em> \u2014 is executed before running each test;\u00a0<\/p>\n<\/li>\n<li>\n<p><em>tearDown<\/em> \u2014 is executed after completion of each test;\u00a0<\/p>\n<\/li>\n<li>\n<p><em>setUpTestData<\/em> \u2014 is executed before running all tests of a particular class.\u00a0<\/p>\n<\/li>\n<\/ol>\n<p>Let&#8217;s use the <a href=\"https:\/\/docs.djangoproject.com\/en\/4.0\/topics\/testing\/tools\/#django.test.TestCase.setUpTestData\"><u>latter<\/u><\/a>. Since it is a method of the class, let&#8217;s add the appropriate decorator. Inside, we create an object of the <em>Trial<\/em> class and get the <em>email<\/em> field from it. We will use the field in the tests.<\/p>\n<pre><code class=\"python\">class TrialTests(TestCase):     \"\"\"Tests for Trial model\"\"\"      @classmethod     def setUpTestData(cls):         \"\"\"Set up the database before running tests of the class\"\"\"          cls.trial = Trial.objects.create(             email='test@gmail.com'         )         cls.email_field = cls.trial._meta.get_field('email')<\/code><\/pre>\n<p>Now, when running tests of the <em>TrialTests<\/em> class, a <em>trial<\/em> object is created in the new database. After the run, the object is deleted.\u00a0<\/p>\n<p>Let&#8217;s write the test of the <em>verbose_name<\/em> parameter.\u00a0<\/p>\n<pre><code class=\"python\">def test_verbose_name(self):     \"\"\"The verbose_name parameter test\"\"\"      real_verbose_name = getattr(self.email_field, 'verbose_name')     expected_verbose_name = 'Email'      self.assertEqual(real_verbose_name, expected_verbose_name)<\/code><\/pre>\n<p>From the <em>email_field<\/em> field we extract the value of the <em>verbose_name<\/em> parameter. Then we apply the <em>assertEqual<\/em> method from the <em>TestCase<\/em> class. The method compares two parameters &#8212; the real and expected values of <em>verbose_name. <\/em>If the values are equal, the test runs successfully. Otherwise, it fails.\u00a0<\/p>\n<p>Let&#8217;s write the same tests for the <em>max_length<\/em> and <em>unique<\/em> parameters.\u00a0<\/p>\n<pre><code class=\"python\">def test_max_length(self):     \"\"\"The max_length parameter test\"\"\"      real_max_length = getattr(self.email_field, 'max_length')      self.assertEqual(real_max_length, 256)  def test_unique(self):     \"\"\"The unique parameter test\"\"\"      real_unique = getattr(self.email_field, 'unique')      self.assertEqual(real_unique, False)<\/code><\/pre>\n<p>It&#8217;s the same as with <em>verbose_name<\/em>.\u00a0<\/p>\n<p>By the way, in the <em>unique<\/em> parameter test, we check that the value is <em>False<\/em>. The <em>assertFalse<\/em> command makes it easier to do. Let&#8217;s rewrite the code of this test.\u00a0<\/p>\n<pre><code class=\"python\">def test_unique(self):     \"\"\"The unique parameter test\"\"\"      real_unique = getattr(self.email_field, 'unique')      self.assertFalse(real_unique)<\/code><\/pre>\n<p>The code is shorter and more readable. By the way, Django has many such <a href=\"https:\/\/docs.djangoproject.com\/en\/3.2\/topics\/testing\/tools\/#assertions\"><u>helpful assertions<\/u><\/a>.\u00a0<\/p>\n<p>Now let&#8217;s check the string representation of the object.\u00a0<\/p>\n<pre><code class=\"python\">def test_string_representation(self):     \"\"\"The __str__ method test\"\"\"      self.assertEqual(str(self.trial), str(self.trial.email))<\/code><\/pre>\n<p>That one&#8217;s easy. We check that the string representation of the object equals its email.\u00a0<\/p>\n<p>And the last thing is the tests of the model fields:\u00a0<\/p>\n<pre><code class=\"python\">def test_model_verbose_name(self):     \"\"\"The test of the verbose_name field of the Trial model\"\"\"      self.assertEqual(Trial._meta.verbose_name, 'Trial')  def test_model_verbose_name_plural(self):     \"\"\"The test of the verbose_name_plural fields of the Trial model\"\"\"      self.assertEqual(Trial._meta.verbose_name_plural, 'Trials')<\/code><\/pre>\n<p>Let&#8217;s access the fields of the <em>Trial<\/em> model through <em>_meta<\/em> and compare their value with the expected one.\u00a0<\/p>\n<p>If you run the tests now, they will run successfully, as before. Well, that&#8217;s no fun! Let&#8217;s break something. Let the <em>verbose_name<\/em> parameter of the <em>Trial<\/em> model become our victim. Open the model&#8217;s code and change the value of this field from &#171;Trial&#187; to &#171;Something else&#187;. Let&#8217;s run the tests.\u00a0<\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<p>As you can see, one of the tests failed. Django tells us about the failure and that the real value of the field (&#171;Something else&#187;) doesn&#8217;t equal the expected value (&#171;Trial&#187;).\u00a0<\/p>\n<h3>Mixins &#8212; the helpful guys<\/h3>\n<p>Model tests are homogeneous. So, when you have a lot of entities, testing them is not the most pleasant routine. I tried to simplify this process somewhat with mixins. My method is not perfect, and I do not insist on using it. However, you may find it useful.\u00a0<\/p>\n<p>I think you noticed that when we test the <em>v<\/em><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-410714","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/410714","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=410714"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/410714\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=410714"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=410714"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=410714"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}