mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-28 13:01:38 +00:00
* bump * Initial function write * Delete this line from yaml * Changed all the main things - Need help on tests * pylint errors * Tests v0 * Tests v1 * Tests v2 --------- Co-authored-by: andrewkenreich <andrew.kenreich@gmail.com>
388 lines
9.1 KiB
Markdown
Vendored
388 lines
9.1 KiB
Markdown
Vendored
# 1. OpenBB Terminal : `Unit Testing` - `Fixtures`
|
|
|
|
In this document we will list custom `pytest fixtures` built in OpenBBTerminal and explain how they works.
|
|
|
|
|
|
## 1.1. What is a `fixture` ?
|
|
This part is fully documented so you can take a look directly on `pytest` documentation right here :
|
|
|
|
https://docs.pytest.org/en/6.2.x/fixture.html
|
|
|
|
## 1.2. Which `fixtures` are available ?
|
|
|
|
You can list all the available `pytest fixtures` using the following command :
|
|
|
|
```
|
|
pytest --fixtures
|
|
```
|
|
|
|
This will include the :
|
|
- `fixtures` available by default in `pytest`
|
|
- `fixtures` defined by installed `pytest plugins`
|
|
- custom `fixtures` built specially for this OpenBBTerminal
|
|
|
|
## 1.3. Which are the custom `fixtures` ?
|
|
|
|
The fixtures built for OpenBBTerminal are here to : make sure contributors are putting `test data file` in the right location.
|
|
|
|
Here we call `test data file` the files that you might use to store data.
|
|
|
|
For instance if you want to test a `function` generating a `Pandas DataFrame`, you might want to :
|
|
- store expected `DataFrame` result inside a `csv`
|
|
- then compare this expected result with the result of that `function`
|
|
|
|
Here is a table listing the fixtures specifically built for this library :
|
|
|
|
|
|
|**Fixture**|**Purpose**|
|
|
|:-|:-|
|
|
|default_csv_path|Builds a default location to store a `csv` file related to a test.|
|
|
|default_txt_path|Builds a default location to store a `txt` file related to a test.|
|
|
|default_json_path|Builds a default location to store a `json` file related to a test.|
|
|
|record_stdout|Capture stdout in a test. And compare it to the last recording.|
|
|
|recorder|Allow capturing one or multiple variable(s) in a test. And compare this captured variable(s) to the last recording|
|
|
|
|
Note that you can't combine `record_stdout` and `recorder` fixtures in the same test.
|
|
|
|
# 2. Path building `fixtures`
|
|
## 2.1. What is the purpose of these `fixtures` ?
|
|
These `fixtures` will provide a location to store your `data file`.
|
|
|
|
The folder to store the `data file` will be generated automatically.
|
|
|
|
The file will be store :
|
|
- inside the same folder than the `test module`
|
|
- inside a folder with the data type : for instance `txt` if you are storing text data
|
|
- inside a file with the name of the `test function` and with the right extension : `.txt` if its a text file.
|
|
|
|
Suppose `XXX` is your data type, the generated path will look like this :
|
|
- module_path/`XXX`/test_module_name/test_function_name.`XXX`
|
|
|
|
This mimic the default location for `vcrpy cassettes`.
|
|
|
|
More on `vcrpy` here :
|
|
https://vcrpy.readthedocs.io/en/latest/
|
|
|
|
## 2.2. How to use `default_csv_path` ?
|
|
|
|
```python
|
|
import pandas as pd
|
|
from tested_module import function_generating_a_dataframe
|
|
|
|
def test_function(default_csv_path):
|
|
result_dataframe = function_generating_a_dataframe()
|
|
|
|
result_dataframe.to_csv(default_csv_path)
|
|
|
|
expected_dataframe = result_dataframe.read_csv(default_csv_path)
|
|
|
|
pd.testing.assert_frame_equal(result_dataframe, expected_dataframe)
|
|
```
|
|
|
|
You might want to comment the file saving part after the first run.
|
|
|
|
Or find a way to conditionally disable it.
|
|
|
|
## 2.3. How to use `default_txt_path` ?
|
|
|
|
```python
|
|
from tested_module import function_generating_a_str
|
|
|
|
def test_function(default_txt_path):
|
|
result_txt = function_generating_a_str()
|
|
|
|
with open(file=default_txt_path, mode="w", encoding="utf-8") as f:
|
|
f.write(result_txt)
|
|
|
|
with open(file=default_txt_path, encoding="utf-8") as f:
|
|
expected_txt = f.read()
|
|
|
|
assert result_txt == expected_txt
|
|
```
|
|
|
|
## 2.4. How to use `default_json_path` ?
|
|
|
|
```python
|
|
import json
|
|
from tested_module import function_generating_a_json
|
|
|
|
def test_function(default_json_path):
|
|
result_json = function_generating_a_json()
|
|
|
|
with open(file=default_txt_path, mode="w", encoding="utf-8") as f:
|
|
json.dump(result_json, f)
|
|
|
|
with open(file=default_json_path, encoding="utf-8") as f:
|
|
expected_json = json.load(f)
|
|
|
|
assert result_json == expected_json
|
|
```
|
|
|
|
|
|
# 3. Recording fixtures
|
|
|
|
|
|
## 3.1. How to use the `record_stdout fixture` ?
|
|
|
|
**USAGE**
|
|
|
|
Example of usage :
|
|
```python
|
|
import pytest
|
|
|
|
@pytest.mark.record_stdout
|
|
def test_function():
|
|
print("Something")
|
|
```
|
|
|
|
This will generate a text file to store the `printed` output.
|
|
|
|
The content of this file will be compared to the `test_function` output at each execution of the test.
|
|
|
|
**RECORD ONCE**
|
|
|
|
You can run this command to generate the text file :
|
|
|
|
```bash
|
|
pytest --record-mode=once
|
|
```
|
|
|
|
This works if :
|
|
- the text file was not generated yet
|
|
- you remove the text file
|
|
|
|
**RECORD REWRITE**
|
|
|
|
You can run this command to regenerate the text file :
|
|
|
|
```bash
|
|
pytest --record_mode=once --rewrite-expected
|
|
```
|
|
|
|
**REWRITE EXPECTED**
|
|
|
|
This will force `record_stdout` to rewrite any changed file (`txt`).
|
|
|
|
This will not rewrite the cassettes.
|
|
|
|
Example :
|
|
```
|
|
pytest --rewrite-expected
|
|
```
|
|
|
|
**VCR**
|
|
|
|
You can combine `record_stdout` and `vcr` fixtures, like this :
|
|
|
|
```python
|
|
import pytest
|
|
import requests
|
|
|
|
@pytest.mark.vcr
|
|
@pytest.mark.record_stdout
|
|
def test_function():
|
|
response = request('https://api.github.com/user', auth=('user', 'pass'))
|
|
|
|
print(response.status_code)
|
|
print(response.text)
|
|
```
|
|
|
|
**ASSERT IN LIST**
|
|
|
|
You are not forced to save a text file, you can use a list of texts instead like this :
|
|
|
|
```python
|
|
import pytest
|
|
|
|
@pytest.mark.record_stdout(
|
|
assert_in_list=["Some text", "Another text"],
|
|
save_record=False,
|
|
)
|
|
def test_function():
|
|
print("""
|
|
This text contains :
|
|
- Some text
|
|
- Another text
|
|
""")
|
|
```
|
|
|
|
|
|
**STRIP**
|
|
|
|
If your `test_function` output a random number of blank before or after you can `strip` the captured data.
|
|
|
|
Example :
|
|
```python
|
|
import pytest
|
|
import random
|
|
|
|
@pytest.mark.record_stdout(strip=True)
|
|
def test_function():
|
|
some_text = "Some text output"
|
|
random_int = random.randint(0,10)
|
|
|
|
for _ in range(random_int):
|
|
text += " "
|
|
|
|
print(some_text)
|
|
```
|
|
|
|
**RECORD MODE**
|
|
|
|
It is possible to programmatically change the `record_mode` on a `test`.
|
|
|
|
Example :
|
|
```python
|
|
import pytest
|
|
|
|
@pytest.mark.record_stdout(record_mode="rewrite")
|
|
def test_function():
|
|
some_text = "Some text output"
|
|
|
|
print(some_text)
|
|
```
|
|
|
|
## 3.2. How to use the `recorder fixture` ?
|
|
|
|
**BEWARE**
|
|
|
|
You can't combine these two fixtures :
|
|
- record_stdout
|
|
- recorder
|
|
|
|
**USAGE**
|
|
|
|
Example of usage :
|
|
```python
|
|
import pandas as pd
|
|
import pytest
|
|
|
|
@pytest.mark.record_stdout
|
|
def test_function(recorder):
|
|
some_dict = {1, 2, 3}
|
|
some_list = [7, 8, 9 ]
|
|
some_tuple = (4, 5, 6)
|
|
some_string = "Some string"
|
|
some_complex_tuple = (pd.DataFrame(), some_dict)
|
|
|
|
recorder.capture(some_dict)
|
|
recorder.capture(some_list)
|
|
recorder.capture(some_string)
|
|
recorder.capture(some_tuple)
|
|
recorder.capture_list(some_complex_tuple)
|
|
```
|
|
|
|
This will generate one or multiple text file(s) to store the `captured` variables.
|
|
|
|
The content of the file(s) will be compared to the `test_function` output at each execution of the test.
|
|
|
|
|
|
|
|
**RECORD ONCE**
|
|
|
|
You can run this command to generate the text file :
|
|
|
|
```bash
|
|
pytest --record-mode=once
|
|
```
|
|
|
|
This works if :
|
|
- the text file was not generated yet
|
|
- you remove the text file
|
|
|
|
**RECORD REWRITE**
|
|
|
|
You can run this command to regenerate the text file :
|
|
|
|
```bash
|
|
pytest --record-mode=rewrite
|
|
```
|
|
|
|
**REWRITE EXPECTED**
|
|
|
|
This will force `recorder` to rewrite any changed file (`csv`, `json`, `txt`).
|
|
|
|
This will not rewrite the cassettes.
|
|
|
|
Example :
|
|
```
|
|
pytest --record_mode=once --rewrite-expected
|
|
```
|
|
|
|
**VCR**
|
|
|
|
You can combine `recorder` and `vcr` fixtures, like this :
|
|
|
|
```python
|
|
import requests
|
|
|
|
@pytest.mark.vcr
|
|
@pytest.mark.record_stdout
|
|
def test_function():
|
|
response = request('https://api.github.com/user', auth=('user', 'pass'))
|
|
|
|
recorder.capture(response.status_code)
|
|
recorder.capture(response.text)
|
|
```
|
|
|
|
**STRIP**
|
|
|
|
If your `test_function` output a random number of blank before or after you can `strip` the captured data.
|
|
|
|
Example :
|
|
```python
|
|
import random
|
|
|
|
def test_function(recorder):
|
|
some_text = "Some text output"
|
|
random_int = random.randint(0,10)
|
|
|
|
for _ in range(random_int):
|
|
text += " "
|
|
|
|
recorder.capture(some_text, strip=True)
|
|
```
|
|
|
|
**RECORD MODE**
|
|
|
|
It is possible to programmatically change the `record_mode` on a `test`.
|
|
|
|
Example :
|
|
```python
|
|
def test_function(recorder):
|
|
some_text = "Some text output"
|
|
|
|
recorder.record_mode = "rewrite"
|
|
recorder.capture(some_text)
|
|
```
|
|
|
|
|
|
**EXTRA ARGUMENTS - PD.TO_CSV**
|
|
If you capture a DataFrame it will be exported as a CSV using the function `pandas.DataFrame.to_csv`.
|
|
|
|
This function contains arguments that can be useful to the test builder.
|
|
|
|
Everything you pass as extra `kwargs` to the `capture` function will be passed to this `to_csv` function.
|
|
|
|
Example :
|
|
```python
|
|
import random
|
|
|
|
def test_function(recorder):
|
|
table_of_floats = pd.DataFrame(
|
|
np.random.uniform(low=1, high=10, size=(5,)),
|
|
columns=["numbers"],
|
|
)
|
|
|
|
recorder.capture(table_of_floats, float_format="%.2f")
|
|
# This will truncate the numbers
|
|
```
|
|
|
|
|
|
**EXTRA ARGUMENTS - JSON.DUMPS**
|
|
If you capture a TUPLE, LIST, DICT... it will be exported as a json using the function `json.dumps`.
|
|
|
|
This function contains arguments that can be useful to the test builder.
|
|
|
|
Everything you pass as extra `kwargs` to the `capture` function will be passed to this `dumps` function.
|