📜 ⬆️ ⬇️

Hypothesis Quick Start Guide

Quick Start Guide


This article is a translation of the Hypothesis - Quick start guide page taken from the official manual.


* Approx. translator: *

I could not find any useful information in Russian on the use of the Hypothesis, except for the speech on November 23, 2017 of Alexander Shorin on "Moscow Python Meetup 50". I decided to figure it out. In the end, something translated. Here, I decided to share.


This document should tell you about all that you need to get started with hypothesis.


Example


Suppose we write a run length encoding system, and want to check that it can.


We have the following code, which I took directly from the Rosetta Code wiki (OK, I deleted some commented code and fixed the formatting, but did not modify the functions):


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 else: entry = (character, count) lst.append(entry) return lst def decode(lst): q = '' for character, count in lst: q += character * count return q 

We want to write a test for this pair of functions that will check some invariant of their job responsibilities.


The invariant when you have this kind of encoding/decoding is that if you encode something and then decode it, you get the same value backwards.


Let's see how this can be done with Hypothesis:


  from hypothesis import given from hypothesis.strategies import text @given(text()) def test_decode_inverts_encode(s): assert decode(encode(s)) == s 

(For this example, we just let pytest detect and run the test. We’ll tell you about other ways you could run it later).


The text function returns what Hypothesis calls the search strategy. An object with methods that describe how to produce and simplify certain kinds of values. Then the decorator @given takes our function test and turns it into parameterized, which, when called, will perform the test function on a wide range of matching data from this strategy.


In any case, this test immediately finds an error in the code:


 Falsifying example: test_decode_inverts_encode(s='') UnboundLocalError: local variable 'character' referenced before assignment 

Note : Local character variable, mentioned before assignment.


Hypothesis correctly indicates that this code is simply incorrect if it is called for an empty string.


If we correct this by simply adding the following code to the beginning of the function, then Hypothesis will tell us that the code is correct (doing nothing, as you expected to pass the test).


 if not input_string: return [] 

If we wanted to make sure that this example would always be checked, we could add it explicitly:


 from hypothesis import given, example from hypothesis.strategies import text @given(text()) @example('') def test_decode_inverts_encode(s): assert decode(encode(s)) == s 

You don’t have to do this, but it can be useful: for clarity, and for a reliable search for examples. Also, as part of local "learning", in any case, the Hypothesis will remember and reuse examples, but for the exchange of data in your continuous integration (CI) system, there is currently no acceptable good workflow.


It is also worth noting that the arguments of the keywords example , and given can be both named and positional. The following code would work just as well:


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

Suppose we had a more interesting error and we forgot to restart the counter
in the loop. Let's say we skipped a string in our encode method:


  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 else: entry = (character, count) lst.append(entry) return lst 

Hypothesis will quickly inform us in the following example:


 Falsifying example: test_decode_inverts_encode(s='001') 

Please note that the presented example is really very simple. Hypothesis is not easy
finds any example for your tests, he knows how to simplify examples
which he finds to create small and easily understandable. In this case, two identical
values ​​are sufficient to set the counter to a number other than one followed by
another value that should have reset the score, but in this case
did not.


Hypothesis examples are valid Python code that you can run. Any arguments that you explicitly specify when calling a function are not generated by the Hypothesis, and if you explicitly provide all the arguments, the Hypothesis will simply call the base function once, rather than run it several times.


Installation


Hypothesis is available on pypi as "hypothesis" . You can install it with:


 pip install hypothesis 

If you want to install directly from source code (for example, because you want to
make changes and install a modified version) you can do it with:


 pip install -e . 

You should probably run the tests first to make sure nothing is broken. You can do it like this:


 python setup.py test 

Please note that if they are not already installed, an attempt will be made to establish test dependencies.


You can do all this in virtualenv .


For example:


 virtualenv venv source venv/bin/activate pip install hypothesis 

Create an isolated environment for you to try Hypothesis without affecting installed system packages.


Test execution


In our example above, we simply allow pytest to detect and run our tests, but we could also run it explicitly ourselves:


  if __name__ == '__main__': test_decode_inverts_encode() 

Or so unittest.TestCase :


  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() 

Note: this works because Hypothesis ignores any arguments that it was not told to provide (positional arguments begin on the right), so the self argument for the test is simply ignored and works as usual. This also means that Hypothesis will play well with other ways of parameterizing tests. For example, it works fine if you use pytest gadgets for some arguments and hypothesis for others.


Writing tests


The test in Hypothesis consists of two parts: a function that looks like a normal test in the selected test structure, but with some additional arguments, and the decorator @given , which indicates how to provide these arguments.


Here are some other examples of how this can be used:


 from hypothesis import given import hypothesis.strategies as st @given(st.integers(), st.integers()) def test_ints_are_commutative(x, y): assert x + y == y + x @given(x=st.integers(), y=st.integers()) def test_ints_cancel(x, y): assert (x + y) - y == x @given(st.lists(st.integers())) def test_reversing_twice_gives_same_list(xs): #      (  0  # 100 ),     . ys = list(xs) ys.reverse() ys.reverse() assert xs == ys @given(st.tuples(st.booleans(), st.text())) def test_look_tuples_work_too(t): #    ,   ,  #     . assert len(t) == 2 assert isinstance(t[0], bool) assert isinstance(t[1], str) 

Notice that, as we saw in the above example, you can pass @given arguments as positional or named.


Where to begin


Now you know enough about the basics to write some tests for your code with Hypothesis. The best way to learn is to do it, so try it.


If you have a tough idea of ​​how to use this kind of test for your code, here are a few tips:


  1. Just try calling the functions with the corresponding random data and get them crashing. You may be surprised how often it works. For example, note that the first error we found in the example of coding did not even reach our statement: it failed because it could not process the data we gave, and not because something wrong had happened.
  2. Look for duplication in your tests. Are there cases when you test the same thing with several different examples? Can you summarize this in one test using Hypothesis?
  3. This part is intended to be implemented on F # , but is still very useful to help in finding good ideas for using Hypothesis.

If you have problems with the launch, do not hesitate and contact asking for help .


Back Next


')

Source: https://habr.com/ru/post/354144/


All Articles