Testing
Last updated
Last updated
For testing purposes, it is recommended to replicate the project structure that we are going to test.
For the example we have, it would look like this:
The directories in the root are the classes or functions that we are going to test, and in the test directory, we have the replica of the original filesystem with the tests already performed.
To properly label the different tests, we need to follow these steps:
Create a file in the root of the project called pytest.ini where we will put all the labels we will use in the project.
In the file created in the previous step, we will configure the different labels to be used in the project.
As shown in the image, we will put the label with a brief description of what the test will do.
**NOTE: When we put this (deselect with '-m "not slow"') in the description of our test, it is to make it skip the tests that have this label, in this case, the slow label, during the global execution of the tests since those tests might take longer. It should be noted that the label does not have to be exactly as shown in the example; it can be any label.
To execute the tests while omitting those labeled as slow, it would be done as follows:**
To let our tests know which label each test belongs to, we have to specify it in the code with the following decorator.
As shown in the image, after mark. we would put our previously configured label in the pytest.ini file.
To perform the respective execution, we must consider the following:
Have the pytest package previously installed: pip install pytest
For best practices, use virtual environments. In the example, we used the virtualenv package; its installation is straightforward:
pip install virtualenv
Create the virtual environment: py -m venv venv
Install the necessary packages for the project
Execute the test: pytest -m arithmetic
In this file, we will create instances of the classes to be tested to centralize the mocks and classes to be used. Below is a brief example.
For this file, we will use the @pytest.fixture
decorator, which provides data or configurations needed for the tests.
Reusability: You can reuse the same setup in multiple tests.
Modularity: Keeps setup code separate from tests.
Maintenance: Easier to modify the test setup without changing all tests.
For creating this example test, we will create a class with the setup method responsible for initializing instances and attributes needed for the different tests. Below is a brief example:
Note: To validate the tests, we will use the assert
keyword, which expects a boolean to know if the condition is met. If it returns false, pytest will return an error message that we can customize.
To read the previously created mocks, we must consider that we will need the following libraries from Python's standard library: unittest
and io
. These will allow us to create and read the data loaded previously in the fixture of the constructor.
Below is a brief example of its use:
With this function, we use patch
from unittest.mock
to temporarily replace the pandas.read_csv
function with a mock version.
return_value=pd.read_csv(StringIO(self.data_mock_csv))
defines what the mock function will return. In this case, it returns the result of reading the CSV from self.data_mock_csv
using pd.read_csv(StringIO(self.data_mock_csv))
.
StringIO(self.data_mock_csv)
converts the text string self.data_mock_csv
into a file-like object that pd.read_csv
can read.
Within the with
context, it calls the load_data_from_csv
method of the self.calculator
instance. The function is designed to load data from a CSV file.
The same applies to JSON:
To create this type of test, we will use the @pytest.mark.parametrize
decorator, which expects the following parameters: "num_1, num_2, result"
, corresponding to the variables used in the test, and [(1, 2, 3), (2, 1, 1), (2, 3, 6), (6, 3, 2)]
, corresponding to the variable initializations and their result. This is equivalent to saying num1 = 1, num2 = 2, and result = 3, and so on with all the data in the list. Note that the data stored in the list must be primitive data, as the idea is only to check the functionality of the method. Below is a brief example:
To create mocks for pytest, it is recommended to use a file called . It should be noted that it is not mandatory to name it this way, but in this case, we will name it so by our convention.
The @pytest.fixture(autouse=True)
decorator with the autouse=True
parameter will initialize the fixtures configured previously in the file to be used in the tests. This decorator works similarly to a beforeAll in jest.js.