Code icon

The App is Under a Quick Maintenance

We apologize for the inconvenience. Please come back later

Menu iconMenu iconPython & SQL Bible
Python & SQL Bible

Chapter 11: Testing in Python

11.2 Mocking and Patching

Mocking is an essential technique in software testing where you replace parts of your system with mock objects and make assertions about how they were used. This approach allows you to simulate the behavior of your system without involving all of its components, which can be time-consuming and inefficient.

To implement mocking, you can use unittest.mock, a library for testing in Python. This library provides an extensive set of tools for creating and using mock objects with ease, allowing you to replace parts of your system under test and make assertions about how they have been used.

A Mock object is a flexible dummy object that acts as a stand-in for a real object. It returns itself whenever you call any method or access any attribute, and it records which methods were called and what the parameters were. This makes it an excellent tool for simulating complex behaviors and testing edge cases in your code, allowing you to catch bugs early in the development process.

Example:

Here's a simple example to show how you might use mocking:

from unittest.mock import Mock

# Create a Mock object
mock = Mock()

# Use the mock
mock.some_method(1, 2, 3)

# Make an assertion about how the mock was used
mock.some_method.assert_called_once_with(1, 2, 3)

Patching is a commonly used technique in software development, especially in unit testing. It allows developers to replace a method or an attribute in a module or a class with a new object, which can be particularly useful when testing code that depends on external systems or resources that may be unavailable or unreliable. By replacing these dependencies with mock objects, developers can simulate the behavior of the external system or resource, allowing them to test their code in isolation and catch potential issues early on.

One important aspect to keep in mind when patching is to ensure that the new object being used as a replacement properly mimics the behavior of the original object being replaced. This can often involve creating a custom mock object that implements the same interface or inherits from the same base class as the original object, and then overriding or mocking the relevant methods or attributes.

In addition to unit testing, patching can also be used in other areas of software development, such as integration testing, where it can help to isolate and test specific components of a larger system. However, it is important to use patching judiciously, as overuse or misuse of this technique can lead to complex and brittle code that is difficult to maintain and debug. As with any tool or technique in software development, it is important to weigh the benefits and drawbacks of patching and to use it in a way that is appropriate for the specific situation at hand.

Here is an example of patching:

pythonCopy code
from unittest.mock import patch

def test_my_function():
    with patch('my_module.MyObject.my_method', return_value=3) as mock_method:
        assert my_function(MyObject()) == 3
    mock_method.assert_called_once()

In this test, we're patching my_method to always return 3, similar to the previous example. However, this time we're patching the method for the entire duration of the with block. Any code inside the with block that calls my_method will use the mock instead of the real method. After the with block, the original method is restored.

The patch function also returns a mock object that we can make assertions on. In this case, we're asserting that the method was called exactly once.

Mocking and patching are powerful tools that allow us to write tests for our code in isolation, leading to faster, more reliable tests. They are essential tools for any Python developer's testing toolkit.

11.2.1 Mock and Side Effects

Mock

In Python, a Mock is a powerful tool that can help you test your code more thoroughly. A Mock object can stand in for another object in your system, allowing you to isolate parts of your code and make sure they're working correctly.

By controlling how the Mock behaves (like specifying its return values or side effects when its methods are called), you can simulate a wide range of scenarios and make sure your code is handling them all correctly. This can help you catch bugs that might otherwise go unnoticed. 

Additionally, by making assertions about how the Mock was used, you can verify that your code is interacting with other parts of your system in the way you expect. All of this can add up to more confidence in your code and fewer bugs in production.

Example:

Here's a simple example of a mock object in action:

pythonCopy code
from unittest.mock import Mock

# Create a mock object
mock = Mock()
mock.return_value = 'hello world'

# Use the mock object
result = mock()

# Check the result
print(result)  # prints: hello world

# Check if the mock was called
print(mock.called)  # prints: True

Side Effects

Another feature of mock objects is that you can set them up to do more than just mimic the behavior of the real object. For example, you can set a mock object to raise an exception when it's called, or to return different values each time it's called.

This is called setting a side effect for the mock. By using side effects, you can thoroughly test how your code handles different scenarios and edge cases. Additionally, you can use mocks to simulate different environments, such as a slow network connection or a database that's offline. 

This allows you to test how your code behaves in a variety of situations, ensuring that it's robust and reliable.

Example:

Here's an example of setting a side effect:

from unittest.mock import Mock

# Create a mock object
mock = Mock()
mock.side_effect = [1, 2, 3, 4, 5]

# Use the mock object
print(mock())  # prints: 1
print(mock())  # prints: 2
print(mock())  # prints: 3

In this example, each call to the mock object returns the next value from the list we specified.

Mocking Methods and Attributes

One important use case for Mock objects is to stand in for methods or attributes on your objects. In addition to the example given in the original text, consider this: you might have an object that relies on a particular file or data source to perform its function.

By mocking the file or data source, you can test your object's behavior without relying on external resources. Alternatively, you could use a mock object to simulate a certain condition, such as a low battery level or a poor network connection, to ensure that your object handles these scenarios gracefully.

The flexibility of Mock objects makes them a powerful tool for testing and ensuring the robustness of your code.

Example:

Here's an example of mocking a method:

from unittest.mock import Mock

class MyObject:
    def my_method(self):
        return 'original value'

# Replace my_method with a mock
MyObject.my_method = Mock(return_value='mocked value')

obj = MyObject()
print(obj.my_method())  # prints: mocked value

In this example, we've replaced the my_method method on MyObject with a mock, so now calling my_method returns the mocked value instead of the original value.

Remember that Mocking and Patching are just tools to isolate your code for unit testing. They should be used sparingly and judiciously, as overuse can lead to tests that are difficult to understand and maintain. But when used appropriately, they can make your tests more reliable, faster, and easier to write.

11.2.2 PyTest

PyTest is a testing framework that allows for more pythonic test writing, which means we can write test cases in a way that is similar to ordinary python scripting. This makes it easier for developers to write tests, as they don't have to learn a new language just to write tests. 

Additionally, PyTest simplifies the process of constructing complex functional test scenarios, allowing developers to focus on writing tests that accurately reflect the functionality of the code they are testing. PyTest is also renowned for its feature-richness and simplicity of use, making it a popular choice among developers of all skill levels.

Furthermore, PyTest is highly extensible, with a wide range of plugins available that can be used to extend its functionality even further. Overall, PyTest is a versatile and powerful testing framework that can greatly simplify the testing process for developers, while also providing a wide range of features and customization options to ensure that tests are accurate and effective.

Example:

import pytest

def add(a, b):
    return a + b

def test_add():
    assert add(2, 3) == 5
    assert add('space', 'ship') == 'spaceship'

You can run the test with pytest command. This will search for files that start with test_ or end with _test and execute any functions that start with test_.

11.2 Mocking and Patching

Mocking is an essential technique in software testing where you replace parts of your system with mock objects and make assertions about how they were used. This approach allows you to simulate the behavior of your system without involving all of its components, which can be time-consuming and inefficient.

To implement mocking, you can use unittest.mock, a library for testing in Python. This library provides an extensive set of tools for creating and using mock objects with ease, allowing you to replace parts of your system under test and make assertions about how they have been used.

A Mock object is a flexible dummy object that acts as a stand-in for a real object. It returns itself whenever you call any method or access any attribute, and it records which methods were called and what the parameters were. This makes it an excellent tool for simulating complex behaviors and testing edge cases in your code, allowing you to catch bugs early in the development process.

Example:

Here's a simple example to show how you might use mocking:

from unittest.mock import Mock

# Create a Mock object
mock = Mock()

# Use the mock
mock.some_method(1, 2, 3)

# Make an assertion about how the mock was used
mock.some_method.assert_called_once_with(1, 2, 3)

Patching is a commonly used technique in software development, especially in unit testing. It allows developers to replace a method or an attribute in a module or a class with a new object, which can be particularly useful when testing code that depends on external systems or resources that may be unavailable or unreliable. By replacing these dependencies with mock objects, developers can simulate the behavior of the external system or resource, allowing them to test their code in isolation and catch potential issues early on.

One important aspect to keep in mind when patching is to ensure that the new object being used as a replacement properly mimics the behavior of the original object being replaced. This can often involve creating a custom mock object that implements the same interface or inherits from the same base class as the original object, and then overriding or mocking the relevant methods or attributes.

In addition to unit testing, patching can also be used in other areas of software development, such as integration testing, where it can help to isolate and test specific components of a larger system. However, it is important to use patching judiciously, as overuse or misuse of this technique can lead to complex and brittle code that is difficult to maintain and debug. As with any tool or technique in software development, it is important to weigh the benefits and drawbacks of patching and to use it in a way that is appropriate for the specific situation at hand.

Here is an example of patching:

pythonCopy code
from unittest.mock import patch

def test_my_function():
    with patch('my_module.MyObject.my_method', return_value=3) as mock_method:
        assert my_function(MyObject()) == 3
    mock_method.assert_called_once()

In this test, we're patching my_method to always return 3, similar to the previous example. However, this time we're patching the method for the entire duration of the with block. Any code inside the with block that calls my_method will use the mock instead of the real method. After the with block, the original method is restored.

The patch function also returns a mock object that we can make assertions on. In this case, we're asserting that the method was called exactly once.

Mocking and patching are powerful tools that allow us to write tests for our code in isolation, leading to faster, more reliable tests. They are essential tools for any Python developer's testing toolkit.

11.2.1 Mock and Side Effects

Mock

In Python, a Mock is a powerful tool that can help you test your code more thoroughly. A Mock object can stand in for another object in your system, allowing you to isolate parts of your code and make sure they're working correctly.

By controlling how the Mock behaves (like specifying its return values or side effects when its methods are called), you can simulate a wide range of scenarios and make sure your code is handling them all correctly. This can help you catch bugs that might otherwise go unnoticed. 

Additionally, by making assertions about how the Mock was used, you can verify that your code is interacting with other parts of your system in the way you expect. All of this can add up to more confidence in your code and fewer bugs in production.

Example:

Here's a simple example of a mock object in action:

pythonCopy code
from unittest.mock import Mock

# Create a mock object
mock = Mock()
mock.return_value = 'hello world'

# Use the mock object
result = mock()

# Check the result
print(result)  # prints: hello world

# Check if the mock was called
print(mock.called)  # prints: True

Side Effects

Another feature of mock objects is that you can set them up to do more than just mimic the behavior of the real object. For example, you can set a mock object to raise an exception when it's called, or to return different values each time it's called.

This is called setting a side effect for the mock. By using side effects, you can thoroughly test how your code handles different scenarios and edge cases. Additionally, you can use mocks to simulate different environments, such as a slow network connection or a database that's offline. 

This allows you to test how your code behaves in a variety of situations, ensuring that it's robust and reliable.

Example:

Here's an example of setting a side effect:

from unittest.mock import Mock

# Create a mock object
mock = Mock()
mock.side_effect = [1, 2, 3, 4, 5]

# Use the mock object
print(mock())  # prints: 1
print(mock())  # prints: 2
print(mock())  # prints: 3

In this example, each call to the mock object returns the next value from the list we specified.

Mocking Methods and Attributes

One important use case for Mock objects is to stand in for methods or attributes on your objects. In addition to the example given in the original text, consider this: you might have an object that relies on a particular file or data source to perform its function.

By mocking the file or data source, you can test your object's behavior without relying on external resources. Alternatively, you could use a mock object to simulate a certain condition, such as a low battery level or a poor network connection, to ensure that your object handles these scenarios gracefully.

The flexibility of Mock objects makes them a powerful tool for testing and ensuring the robustness of your code.

Example:

Here's an example of mocking a method:

from unittest.mock import Mock

class MyObject:
    def my_method(self):
        return 'original value'

# Replace my_method with a mock
MyObject.my_method = Mock(return_value='mocked value')

obj = MyObject()
print(obj.my_method())  # prints: mocked value

In this example, we've replaced the my_method method on MyObject with a mock, so now calling my_method returns the mocked value instead of the original value.

Remember that Mocking and Patching are just tools to isolate your code for unit testing. They should be used sparingly and judiciously, as overuse can lead to tests that are difficult to understand and maintain. But when used appropriately, they can make your tests more reliable, faster, and easier to write.

11.2.2 PyTest

PyTest is a testing framework that allows for more pythonic test writing, which means we can write test cases in a way that is similar to ordinary python scripting. This makes it easier for developers to write tests, as they don't have to learn a new language just to write tests. 

Additionally, PyTest simplifies the process of constructing complex functional test scenarios, allowing developers to focus on writing tests that accurately reflect the functionality of the code they are testing. PyTest is also renowned for its feature-richness and simplicity of use, making it a popular choice among developers of all skill levels.

Furthermore, PyTest is highly extensible, with a wide range of plugins available that can be used to extend its functionality even further. Overall, PyTest is a versatile and powerful testing framework that can greatly simplify the testing process for developers, while also providing a wide range of features and customization options to ensure that tests are accurate and effective.

Example:

import pytest

def add(a, b):
    return a + b

def test_add():
    assert add(2, 3) == 5
    assert add('space', 'ship') == 'spaceship'

You can run the test with pytest command. This will search for files that start with test_ or end with _test and execute any functions that start with test_.

11.2 Mocking and Patching

Mocking is an essential technique in software testing where you replace parts of your system with mock objects and make assertions about how they were used. This approach allows you to simulate the behavior of your system without involving all of its components, which can be time-consuming and inefficient.

To implement mocking, you can use unittest.mock, a library for testing in Python. This library provides an extensive set of tools for creating and using mock objects with ease, allowing you to replace parts of your system under test and make assertions about how they have been used.

A Mock object is a flexible dummy object that acts as a stand-in for a real object. It returns itself whenever you call any method or access any attribute, and it records which methods were called and what the parameters were. This makes it an excellent tool for simulating complex behaviors and testing edge cases in your code, allowing you to catch bugs early in the development process.

Example:

Here's a simple example to show how you might use mocking:

from unittest.mock import Mock

# Create a Mock object
mock = Mock()

# Use the mock
mock.some_method(1, 2, 3)

# Make an assertion about how the mock was used
mock.some_method.assert_called_once_with(1, 2, 3)

Patching is a commonly used technique in software development, especially in unit testing. It allows developers to replace a method or an attribute in a module or a class with a new object, which can be particularly useful when testing code that depends on external systems or resources that may be unavailable or unreliable. By replacing these dependencies with mock objects, developers can simulate the behavior of the external system or resource, allowing them to test their code in isolation and catch potential issues early on.

One important aspect to keep in mind when patching is to ensure that the new object being used as a replacement properly mimics the behavior of the original object being replaced. This can often involve creating a custom mock object that implements the same interface or inherits from the same base class as the original object, and then overriding or mocking the relevant methods or attributes.

In addition to unit testing, patching can also be used in other areas of software development, such as integration testing, where it can help to isolate and test specific components of a larger system. However, it is important to use patching judiciously, as overuse or misuse of this technique can lead to complex and brittle code that is difficult to maintain and debug. As with any tool or technique in software development, it is important to weigh the benefits and drawbacks of patching and to use it in a way that is appropriate for the specific situation at hand.

Here is an example of patching:

pythonCopy code
from unittest.mock import patch

def test_my_function():
    with patch('my_module.MyObject.my_method', return_value=3) as mock_method:
        assert my_function(MyObject()) == 3
    mock_method.assert_called_once()

In this test, we're patching my_method to always return 3, similar to the previous example. However, this time we're patching the method for the entire duration of the with block. Any code inside the with block that calls my_method will use the mock instead of the real method. After the with block, the original method is restored.

The patch function also returns a mock object that we can make assertions on. In this case, we're asserting that the method was called exactly once.

Mocking and patching are powerful tools that allow us to write tests for our code in isolation, leading to faster, more reliable tests. They are essential tools for any Python developer's testing toolkit.

11.2.1 Mock and Side Effects

Mock

In Python, a Mock is a powerful tool that can help you test your code more thoroughly. A Mock object can stand in for another object in your system, allowing you to isolate parts of your code and make sure they're working correctly.

By controlling how the Mock behaves (like specifying its return values or side effects when its methods are called), you can simulate a wide range of scenarios and make sure your code is handling them all correctly. This can help you catch bugs that might otherwise go unnoticed. 

Additionally, by making assertions about how the Mock was used, you can verify that your code is interacting with other parts of your system in the way you expect. All of this can add up to more confidence in your code and fewer bugs in production.

Example:

Here's a simple example of a mock object in action:

pythonCopy code
from unittest.mock import Mock

# Create a mock object
mock = Mock()
mock.return_value = 'hello world'

# Use the mock object
result = mock()

# Check the result
print(result)  # prints: hello world

# Check if the mock was called
print(mock.called)  # prints: True

Side Effects

Another feature of mock objects is that you can set them up to do more than just mimic the behavior of the real object. For example, you can set a mock object to raise an exception when it's called, or to return different values each time it's called.

This is called setting a side effect for the mock. By using side effects, you can thoroughly test how your code handles different scenarios and edge cases. Additionally, you can use mocks to simulate different environments, such as a slow network connection or a database that's offline. 

This allows you to test how your code behaves in a variety of situations, ensuring that it's robust and reliable.

Example:

Here's an example of setting a side effect:

from unittest.mock import Mock

# Create a mock object
mock = Mock()
mock.side_effect = [1, 2, 3, 4, 5]

# Use the mock object
print(mock())  # prints: 1
print(mock())  # prints: 2
print(mock())  # prints: 3

In this example, each call to the mock object returns the next value from the list we specified.

Mocking Methods and Attributes

One important use case for Mock objects is to stand in for methods or attributes on your objects. In addition to the example given in the original text, consider this: you might have an object that relies on a particular file or data source to perform its function.

By mocking the file or data source, you can test your object's behavior without relying on external resources. Alternatively, you could use a mock object to simulate a certain condition, such as a low battery level or a poor network connection, to ensure that your object handles these scenarios gracefully.

The flexibility of Mock objects makes them a powerful tool for testing and ensuring the robustness of your code.

Example:

Here's an example of mocking a method:

from unittest.mock import Mock

class MyObject:
    def my_method(self):
        return 'original value'

# Replace my_method with a mock
MyObject.my_method = Mock(return_value='mocked value')

obj = MyObject()
print(obj.my_method())  # prints: mocked value

In this example, we've replaced the my_method method on MyObject with a mock, so now calling my_method returns the mocked value instead of the original value.

Remember that Mocking and Patching are just tools to isolate your code for unit testing. They should be used sparingly and judiciously, as overuse can lead to tests that are difficult to understand and maintain. But when used appropriately, they can make your tests more reliable, faster, and easier to write.

11.2.2 PyTest

PyTest is a testing framework that allows for more pythonic test writing, which means we can write test cases in a way that is similar to ordinary python scripting. This makes it easier for developers to write tests, as they don't have to learn a new language just to write tests. 

Additionally, PyTest simplifies the process of constructing complex functional test scenarios, allowing developers to focus on writing tests that accurately reflect the functionality of the code they are testing. PyTest is also renowned for its feature-richness and simplicity of use, making it a popular choice among developers of all skill levels.

Furthermore, PyTest is highly extensible, with a wide range of plugins available that can be used to extend its functionality even further. Overall, PyTest is a versatile and powerful testing framework that can greatly simplify the testing process for developers, while also providing a wide range of features and customization options to ensure that tests are accurate and effective.

Example:

import pytest

def add(a, b):
    return a + b

def test_add():
    assert add(2, 3) == 5
    assert add('space', 'ship') == 'spaceship'

You can run the test with pytest command. This will search for files that start with test_ or end with _test and execute any functions that start with test_.

11.2 Mocking and Patching

Mocking is an essential technique in software testing where you replace parts of your system with mock objects and make assertions about how they were used. This approach allows you to simulate the behavior of your system without involving all of its components, which can be time-consuming and inefficient.

To implement mocking, you can use unittest.mock, a library for testing in Python. This library provides an extensive set of tools for creating and using mock objects with ease, allowing you to replace parts of your system under test and make assertions about how they have been used.

A Mock object is a flexible dummy object that acts as a stand-in for a real object. It returns itself whenever you call any method or access any attribute, and it records which methods were called and what the parameters were. This makes it an excellent tool for simulating complex behaviors and testing edge cases in your code, allowing you to catch bugs early in the development process.

Example:

Here's a simple example to show how you might use mocking:

from unittest.mock import Mock

# Create a Mock object
mock = Mock()

# Use the mock
mock.some_method(1, 2, 3)

# Make an assertion about how the mock was used
mock.some_method.assert_called_once_with(1, 2, 3)

Patching is a commonly used technique in software development, especially in unit testing. It allows developers to replace a method or an attribute in a module or a class with a new object, which can be particularly useful when testing code that depends on external systems or resources that may be unavailable or unreliable. By replacing these dependencies with mock objects, developers can simulate the behavior of the external system or resource, allowing them to test their code in isolation and catch potential issues early on.

One important aspect to keep in mind when patching is to ensure that the new object being used as a replacement properly mimics the behavior of the original object being replaced. This can often involve creating a custom mock object that implements the same interface or inherits from the same base class as the original object, and then overriding or mocking the relevant methods or attributes.

In addition to unit testing, patching can also be used in other areas of software development, such as integration testing, where it can help to isolate and test specific components of a larger system. However, it is important to use patching judiciously, as overuse or misuse of this technique can lead to complex and brittle code that is difficult to maintain and debug. As with any tool or technique in software development, it is important to weigh the benefits and drawbacks of patching and to use it in a way that is appropriate for the specific situation at hand.

Here is an example of patching:

pythonCopy code
from unittest.mock import patch

def test_my_function():
    with patch('my_module.MyObject.my_method', return_value=3) as mock_method:
        assert my_function(MyObject()) == 3
    mock_method.assert_called_once()

In this test, we're patching my_method to always return 3, similar to the previous example. However, this time we're patching the method for the entire duration of the with block. Any code inside the with block that calls my_method will use the mock instead of the real method. After the with block, the original method is restored.

The patch function also returns a mock object that we can make assertions on. In this case, we're asserting that the method was called exactly once.

Mocking and patching are powerful tools that allow us to write tests for our code in isolation, leading to faster, more reliable tests. They are essential tools for any Python developer's testing toolkit.

11.2.1 Mock and Side Effects

Mock

In Python, a Mock is a powerful tool that can help you test your code more thoroughly. A Mock object can stand in for another object in your system, allowing you to isolate parts of your code and make sure they're working correctly.

By controlling how the Mock behaves (like specifying its return values or side effects when its methods are called), you can simulate a wide range of scenarios and make sure your code is handling them all correctly. This can help you catch bugs that might otherwise go unnoticed. 

Additionally, by making assertions about how the Mock was used, you can verify that your code is interacting with other parts of your system in the way you expect. All of this can add up to more confidence in your code and fewer bugs in production.

Example:

Here's a simple example of a mock object in action:

pythonCopy code
from unittest.mock import Mock

# Create a mock object
mock = Mock()
mock.return_value = 'hello world'

# Use the mock object
result = mock()

# Check the result
print(result)  # prints: hello world

# Check if the mock was called
print(mock.called)  # prints: True

Side Effects

Another feature of mock objects is that you can set them up to do more than just mimic the behavior of the real object. For example, you can set a mock object to raise an exception when it's called, or to return different values each time it's called.

This is called setting a side effect for the mock. By using side effects, you can thoroughly test how your code handles different scenarios and edge cases. Additionally, you can use mocks to simulate different environments, such as a slow network connection or a database that's offline. 

This allows you to test how your code behaves in a variety of situations, ensuring that it's robust and reliable.

Example:

Here's an example of setting a side effect:

from unittest.mock import Mock

# Create a mock object
mock = Mock()
mock.side_effect = [1, 2, 3, 4, 5]

# Use the mock object
print(mock())  # prints: 1
print(mock())  # prints: 2
print(mock())  # prints: 3

In this example, each call to the mock object returns the next value from the list we specified.

Mocking Methods and Attributes

One important use case for Mock objects is to stand in for methods or attributes on your objects. In addition to the example given in the original text, consider this: you might have an object that relies on a particular file or data source to perform its function.

By mocking the file or data source, you can test your object's behavior without relying on external resources. Alternatively, you could use a mock object to simulate a certain condition, such as a low battery level or a poor network connection, to ensure that your object handles these scenarios gracefully.

The flexibility of Mock objects makes them a powerful tool for testing and ensuring the robustness of your code.

Example:

Here's an example of mocking a method:

from unittest.mock import Mock

class MyObject:
    def my_method(self):
        return 'original value'

# Replace my_method with a mock
MyObject.my_method = Mock(return_value='mocked value')

obj = MyObject()
print(obj.my_method())  # prints: mocked value

In this example, we've replaced the my_method method on MyObject with a mock, so now calling my_method returns the mocked value instead of the original value.

Remember that Mocking and Patching are just tools to isolate your code for unit testing. They should be used sparingly and judiciously, as overuse can lead to tests that are difficult to understand and maintain. But when used appropriately, they can make your tests more reliable, faster, and easier to write.

11.2.2 PyTest

PyTest is a testing framework that allows for more pythonic test writing, which means we can write test cases in a way that is similar to ordinary python scripting. This makes it easier for developers to write tests, as they don't have to learn a new language just to write tests. 

Additionally, PyTest simplifies the process of constructing complex functional test scenarios, allowing developers to focus on writing tests that accurately reflect the functionality of the code they are testing. PyTest is also renowned for its feature-richness and simplicity of use, making it a popular choice among developers of all skill levels.

Furthermore, PyTest is highly extensible, with a wide range of plugins available that can be used to extend its functionality even further. Overall, PyTest is a versatile and powerful testing framework that can greatly simplify the testing process for developers, while also providing a wide range of features and customization options to ensure that tests are accurate and effective.

Example:

import pytest

def add(a, b):
    return a + b

def test_add():
    assert add(2, 3) == 5
    assert add('space', 'ship') == 'spaceship'

You can run the test with pytest command. This will search for files that start with test_ or end with _test and execute any functions that start with test_.