Testing is important no matter which programming language or framework you use. hypothesis is an advanced testing library for Python. It allows writing test cases with parameters, and then generating easy-to-understand test data that makes the test fail. The testing library covers most cases and can really help you find bugs in your code.

This article shows how to use Hypothesis for testing in Python, and provides some examples.

How do we differentiate the tests?

Before we get started with attribute-based testing, we need to know the general differences in testing. There are different approaches to group testing, the two most common approaches are based on test methods and test levels. Let's start with the test levels that most people have already heard of. Essentially, there are four test levels (although one may know or define others as well):

  • Unit Testing

  • Integration Testing

  • System Testing

  • End-to-end testing

Different test levels focus on different things. Unit testing focuses on a specific part or feature of the software. This can be a single feature or part of a feature. Conversely, integration testing focuses on collaboration through the interfaces of software components. System testing goes even further and can test the entire system.

Now, we will look at the wide variety of testing methods that exist.

The most common and known are static and dynamic testing. Static testing is the process of statically checking for possible errors in the program code, interface or documentation without actually running the software under test. If the software or its parts are actually executed, we call it dynamic testing. Writing unit tests and integration tests are dynamic tests.

Another common approach is the boxed approach. Basically, it can be divided into white-box testing and black-box testing (and gray-box testing as a mix of both). White-box testing verifies the internal structure or working of a program. Black-box testing is the opposite, in which the application is treated as a black box and tested for its interactions. This means testing functionality without knowledge of the internal implementation.

What is attribute-based testing?

Now that we've taken a quick look at how to differentiate tests, you may be asking: What is property-based testing?

Property-based testing refers to writing logical statements (i.e., "properties") that are true for your code, and then using an automated tool to generate test input (generally, some specific type of randomly generated input data) and observe whether the program whether the properties remain unchanged when the program accepts that input. If an input violates a property, the user proves that there is an error in the program and finds a convenient example that demonstrates the error.

Attribute based testing using Hypothesis

Let's take a simple example. Suppose you have two functions crement() and decrement(). A sample implementation might look like this:

# increment_decrement.py

def increment(number: int) -> int:
    return number + 1

def decrement(number: int) -> int:
    return number - 1

You might write unit test code for both, as follows:

# test_increment_decrement_pytest.py

from increment_decrement import decrement
from increment_decrement import increment

def test_increment():
    x = 5
    expected = 6
    actual = increment(x)
    assert actual == expected

def test_decrement():
    x = 5
    expected = 4
    actual = decrement(x)
    assert actual == expected

Note: The test code is written using the pytest framework.

Of course, you can write more test scripts to test two functions with different values, or even parameterize the tests. However, in the end you will test both functions using predefined values.

Writing tests using attribute-based test libraries (e.g. Hypothesis) is different. Here, you specify the type to be tested and how the software works or behaves. The library then generates random values based on the specified types to actually test the functionality.

Let's see how we can use Hypothesis to test two of our features.

# test_increment_decrement_hypothesis.py

from hypothesis import given
import hypothesis.strategies as st

from increment_decrement import decrement
from increment_decrement import increment

def test_increment(x):
    expected = x + 1
    actual = increment(x)
    assert actual == expected

def test_decrement(x):
    expected = x - 1
    actual = decrement(x)
    assert actual == expected

As you can see, both test scripts have an argument x. The value of x is generated by Hypothesis using the integers() method. Hypothesis provides various methods. Essentially, these methods correspond to built-in types or other structures, and generate random data that matches the given type.

Sounds good, doesn't it? But what if we want to test a function with a particular value to make sure it can also use that value? Hypothesis provides an @example() decorator in which you can define a value that can be passed to the corresponding function even if that value does not belong to the randomly generated test data set.

Let's take a simple example:

# div.py

def div(dividend: int, divisor: int) -> int:
    return dividend // divisor

We define a function div() that takes a divisor and a divisor and returns the quotient of both. Note that both arguments are integer data, so the result should also be integer data, and we use Python's // operator to perform integer division.

To test the div() function, we created a new test file test_div.py and wrote a test script called test_div().

# test_div.py

from hypothesis import example
from hypothesis import given
import hypothesis.strategies as st

from div import div

@given(dividend=st.integers(), divisor=st.integers())
def test_div(dividend, divisor):
    if divisor == 0:
        expected = -1
        expected = dividend // divisor
    actual = div(dividend, divisor)
    assert actual == expected

Similarly, we use Hypothesis' integers() method to generate the values of the divisor and the divisor. The test script we write may or may not pass, depending on the values generated by Hypothesis at the time of execution. To ensure that the value 0 is always passed to the div() function, we add @example(1, 0) to the test_div() function. Thus, even if div() is not in the randomly generated dataset, it will be called at least once with the value 0 of the divisor.

If we run the test script as is, test_div() will always fail. So, let's modify the div() function to handle this situation and make the test pass:

# div.py

def div(dividend: int, divisor: int) -> int:
    if divisor == 0:
        return -1
    return dividend // divisor


This article focuses on what attribute-based tests are and why they are useful. In addition, you take a quick look at the Hypothesis library, which enables you to write attribute-based tests and execute them together with pytest tests.

Related articles

mplcyberpunk tutorials

Cyberpunk 2077" is a very popular single-player game ~ The game is set in the year 2077, a highly developed technology but chaotic and disorderly "cyberpunk" city. In this world, although the technology is highly developed, but the standard of living of

Heartrate: Real-time Visualization for Python Execution

Heartate - Track programs like a heart rate,Heartrate is a Python tool library that allows you to visualize the execution of Python programs in real time. Monitoring a running Python program is shown in the following figure.

Implementing reverse proxies with Django only

When you think of reverse proxies, you say nginx. nginx is the ideal reverse proxy tool. But now the conditions are tough. The server doesn't have nginx and doesn't have root privileges, which means you can't compile and install nginx, and only one port,

What does join mean in python?

Python has two functions .join() and os.path.join(), which do the following: . join(): Concatenate string arrays. Concatenates the elements of a string, a tuple, and a list with the specified characters (separator) to generate a new string os.path.join()

Add new content to the python dict

Adding new content to a dictionary is done by adding new key/value pairs, modifying or deleting existing key/value pairs as in the following examples

How to delete elements in python dict

clear() method is used to clear all the data in the dictionary, because it is an in-place operation, so it returns None (also can be interpreted as no return value)

python crawler how to get cookies

Cookie, refers to the data (usually encrypted) that some websites store on the user's local terminal in order to identify the user and perform session tracking. For example, some websites require login to access a page, before login, you want to grab the

How OpenCV tracks objects in video

Each frame of the video is a picture, tracking an object in the video, decomposition down, in fact, is to find that object in each frame of the picture.