How python Automates Unit Tests with Hypothesis

  • 2021-10-13 08:05:16
  • OfStack

Basic knowledge of directory Hypothesis
Hypothesis Quick Start
1. Install 2. Use 3. Where do other policy references start
References

High-quality code can not be separated from unit testing, and the design of unit test use cases is often time-consuming, and it is difficult to think of one extreme case. This article describes how to use Hypothesis to automate unit testing

Students who have brushed the problem of force deduction algorithm know that sometimes they feel that the code is perfect, and they find that many situations have not been considered when they are submitted. Then sigh that the unit test of force buckle is really better than cattle.

Therefore, high-quality code cannot be separated from unit testing. If you haven't written unit testing yet, it is recommended to learn the following commonly used unit test libraries first. Only by practicing it can you feel the pain points mentioned at the beginning of this article.

Hypothesis is an Python library designed to make unit tests easier to write, more powerful at run time, and to find extreme situations in your code that you wouldn't expect. It is stable, powerful and easy to add to any existing testing framework. It works by letting you write tests that assert that every situation should be correct, not just those you happen to think of.

Basic knowledge of Hypothesis

A typical unit test needs to write a few test cases, then write a test function, run it through a piece of code, and then check the results against the expected results.

Hypothesis is different. It is unit testing based on attributes. It does this by generating any data that matches your specification and checking whether the program is still valid in this case. If it finds a failed use case, it takes the example and reduces its test case scope to 1 size, then simplifies it until it finds a much smaller example that will still cause problems. They are then saved, and they will still be used in subsequent unit tests.

Now let's see how to use it.

Hypothesis Quick Start

1. Installation

You can install through pip, source code [2], or some extensions [3], as follows:


pip install hypothesis
pip install hypothesis[pandas,django]

2. Use

First write a piece of code, save in mycode. py, the function is to encode and decode the string specifically, as follows:


def encode(input_string):
 count = 1
 prev = ""
 lst = []
 for character in input_string:
  if character != prev:
   if prev:
    entry = (prev, count)
    lst.append(entry)
   count = 1
   prev = character
  else:
   count += 1
 entry = (character, count)
 lst.append(entry)
 return lst


def decode(lst):
 q = ""
 for character, count in lst:
  q += character * count
 return q

Unit testing this code often requires writing a lot of test cases. Now we use hypothesis to test it automatically for us, and write test_mycode. py (file name is optional), which reads as follows:


from hypothesis import given
from mycode import decode,encode
from hypothesis.strategies import text
import unittest


class TestEncoding(unittest.TestCase):
 @given(text())
 def test_decode_inverts_encode(self, s):
  self.assertEqual(decode(encode(s)), s)


if __name__ == "__main__":
 unittest.main()

It can be seen that there is no specific test case here, but the strategy of using text is equivalent to the possible situation of hypothesis automatic exhaustion. It can also be seen that it can be easily integrated with other test frameworks, here is unittest. Now run 1 to see the effect:


(py38env) ➜ tmp python test_mycode.py
Falsifying example: test_decode_inverts_encode(
 self=<__main__.TestEncoding testMethod=test_decode_inverts_encode>, s='',
)
E
======================================================================
ERROR: test_decode_inverts_encode (__main__.TestEncoding)
----------------------------------------------------------------------
Traceback (most recent call last):
 File "test_mycode.py", line 9, in test_decode_inverts_encode
 def test_decode_inverts_encode(self, s):
 File "/Users/aaron/py38env/lib/python3.8/site-packages/hypothesis/core.py", line 1162, in wrapped_test
 raise the_error_hypothesis_found
 File "test_mycode.py", line 10, in test_decode_inverts_encode
 self.assertEqual(decode(encode(s)), s)
 File "/Users/aaron/tmp/mycode.py", line 14, in encode
 entry = (character, count)
UnboundLocalError: local variable 'character' referenced before assignment

----------------------------------------------------------------------
Ran 1 test in 0.048s

FAILED (errors=1)

Here, it is tested that an UnboundLocalError exception will be thrown when the string is ''. Now let's fix this bug and print out all the test cases s to see which test cases it uses.

The encode function adds the following code:


if not input_string:
 return []

The test_mycode. py file prints out test cases:


@given(text())
def test_decode_inverts_encode(self, s):
 print(f"{s=}")
 self.assertEqual(decode(encode(s)), s)

Execute again:


(py38env) ➜ tmp python test_mycode.py
s=''
s='1'
s='0'
s='0'
s='0'
s='Ā'
s='\U000cf5e5'
s='0'
s=''
s='0'
s='0'
s='E'
s=")d ① '\x18\U0003deb3 I jd"
s='\U0005bc37\x07\U000537a1ÝÀãiÎ\U000ce9e5\x0b'
s='\U0005bc37\U0005bc37\U000537a1ÝÀãiÎ\U000ce9e5\x0b'
s='\U0005bc37\U000537a1\U000537a1ÝÀãiÎ\U000ce9e5\x0b'
s='À\U000537a1\U000537a1ÝÀãiÎ\U000ce9e5\x0b'
s='\U000965e1\x12\x85&\U000f500aÄÃc'
s='\n\U0004466c\x86Î\x07'
s='Ê\U00063f1e\x01G\x88'
s='ÚV\n'
s='VV\n'
s='\U0008debf è '
s='\U0008debf è '
s='\U0008debf In fact, in fact, the '
s='\U0008debf\U0008debf'
s='\U0008debf\U0008debf ó ]½ à q\x82#\U00015196\U0001c8beg'
s='\U0008debfg ó ]½ à q\x82#\U00015196\U0001c8beg'
s='?'
s='Î'
s='Î\U00085b9e'
s="Î8'?\U00057c38Ù;\x07\U000a5ea8Ò»=\U00091d5b~8뺈"
s='\U000d6497Ý>'
s='\U000e0f01'
s='\U000e0f01Å0y¢KN®'
s='\U000e0f01Å0y¢KN®'
s='\U00050a06'
s='Å\U000b98b3 Wa \U000ba80a á `Ã-Êu\x8c\x90³FÔ"'
s='\x8e\U0004612a\x83ç'
s='\x8e'
s='\x8e\x98\U000fb3e0\U0010d2b3\x10\x82\x94Ð W '
s='¥W'
s='p\U000e5a2aE · ` § '
s='\U000b80f8\x12\U000c2d54'
s='.\U000703de'
s='6\U00010ffa\U000f7994\x8e'
s='116\U000f7994\x8e'
s='1?6\U000f7994\x8e'
s='4?6\U000f7994\x8e'
s='4\x8e6\U000f7994\x8e'
s='0'
s='\U0006a564´Ð\x93 ü \x9eb&i\x1cÑ'
s='\U000ceb6f'
s='\U000ceb6f\xa0\x08'
s='\U000ceb6f\xa0\x08'
s='\U000ceb6fꄃ\x08'
s='\U000ceb6fꄃ Uniform \U0007cc15\U000b2aaa × **'
s='\U000ceb6fꄃ Uniform '
s=' Uniform ꄃ Uniform '
s='J\x14?ö'
s='q)'
s='q)'
s='q\U00060931'
s='q6'
s='\U000e3441'
s='\U000e3441\U00019958¯'
s='\x13'
s='\U000f34dbk'
s='Kp&tÛ à '
s='\nö\x93'
s='\n\n\x93'
s='\U00019c8dѳ\U00056cbd\U000e3b2f\U00058d302'
s='\x90=R\x8bß\x03'
s='\x9a'
s='\U000147e7'
s='\U000147e7\x85\U0007a3ef'
s='\U000147e7\U00050a070Â>'
s='\U000a4089\x0eC+RÁ\x02\x97\x9c ü ÌïSS\U0006cbc5;ÿ~\x16\x019VÇ\U000a32fdQ ÷ \x15'
s='ÞÚ¾\x19©Z®'
s='ਸ਼æ'
s='\U000cd45a'
s='\U000cd45a\U000e15cbÑ\x08J\ueb3e ú ß\x07I\x91\x9a\x18\x16Ç\x80\x1a'
s='\x8f}º\x0eq\x0b'
s='\x0e}º\x0eq\x0b'
s="\U000e05a3&¶º[fõ\x8bÜR'ͼt\x97 í W\x05\U000caea9\U0008fd74\U000e8f1c¹?dfƾ\x13"
s='\x10\U000e12e2 ① \U0006f96erý\U00014baf\x00\x95\U000dbc92É\U00081613µ\U0003b865Z\U0008cc3c'
s=' ú \U000b561f\x8fÎ'
s='\t à Ö ÷ '
s=' à \x92©Ì\U000618fa\x92'
s='\U000aaf94\x94\x84\U000cda69\U0005291a\U000a63deþ¿O\x8a>\U000b458bÊ.\U00086f07\x1a'
s='\U0009754e?U_\xa0\x13PQ\x18º\x07\U0006c9c5.Á'
s='\U00102456'
s='³WᵎÕ'
s='\x14\x1c'
s='\x14'
s='\x14\U00105bcd"\x10Ô\x99\U000a5032R\U00056c44V& ÷ >+\U000aaff2ñ®\U000d7570%ª!\U00032553´8x^«'
s='\x00\U000e2ac4¼ÄUrB'
s='\x00\U000e2ac4¼ÄUrB'
s='\x00\U000e2ac4¼ÄUrB'
s='ª\x1aU\x8aÇ\U000b2fb9\U0005a586'
.
----------------------------------------------------------------------
Ran 1 test in 0.180s

OK

It can be seen from the execution results that ''is tested first, and then hypothesis uses a large number of extreme test cases, which reduces the burden of handwriting and greatly improves the efficiency.

Although hypothesis has automatic memory, you can still explicitly specify a test case 1 to be tested directly, and this is recommended. For example, I want to test ''in every test, which can be written as follows:


from hypothesis import given, example
from hypothesis.strategies import text


@given(text())
@example("")
def test_decode_inverts_encode(s):
 assert decode(encode(s)) == s

This one point is very useful in improving the readability of the test code, and can be used to tell the developer or future self that the input string must consider the ''situation.

In addition, to perform unit tests, it is not necessary to use unittest. main (), which can also be done. Is it very convenient:


if __name__ == "__main__":
 test_decode_inverts_encode()

3. Other strategy references

Where to start

The above is just to attract jade. hypothesis has many automatic features, so it is no longer listed in 11. The best learning method is to try while doing it. hypothesis is an open source project with detailed official documentation [4], and the GitHub repository [5]. This is a good place for you to start automated testing:

References

[1]

Library: https://realpython.com/python-testing/

[2]

Source code installation: https://github.com/HypothesisWorks/hypothesis/blob/master/CONTRIBUTING.rst

[3]

Extension: https://hypothesis.readthedocs.io/en/latest/extras. html

[4]

Official document: https://hypothesis.readthedocs. io/en/latest/quickstart. html # running-tests

[5]

GitHub Warehouse: https://github.com/HypothesisWorks/hypothesis/

These are the details of how python automates unit tests with Hypothesis. For more information about python automating unit tests with Hypothesis, please pay attention to other related articles on this site!


Related articles: