mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-27 04:31:13 +00:00
* Updating tests : conftest + pytest.ini * Updating tests : stocks/prediction_techniques * Updating tests : doc
415 lines
11 KiB
Markdown
Vendored
415 lines
11 KiB
Markdown
Vendored
# 1. Gamestonk Terminal : `Unit Testing`
|
|
|
|
This document is part of the `Gamestonk Terminal` library documentation.
|
|
|
|
It aims to provide necessary information in order :
|
|
- Run `unit tests`
|
|
- Build `unit tests`
|
|
- Maintain `unit tests`
|
|
|
|
## 1.1. Why having unit tests ?
|
|
|
|
Here `Unit tests` purpose is to allow : update without fear.
|
|
|
|
Insuring with enough confidence that : addition, removal or update of a `module/function` won't break other parts of the code.
|
|
|
|
To do that we need `unit tests` to be :
|
|
- fast to write
|
|
- fast to run
|
|
|
|
**FAST TO WRITE**
|
|
|
|
The `unit tests` will only tests the most common cases.
|
|
|
|
**FAST TO RUN**
|
|
|
|
Slow part of the code like `network` or `database` access should be either `mocked` or `marked` as `slow`.
|
|
|
|
# 2. Run `unit tests`
|
|
|
|
In this section we will explain every think you need to run the `unit tests` on `Gamestonk Terminal`.
|
|
|
|
## 2.1. How to install tests dependencies ?
|
|
|
|
To run the tests you will need the `dev-dependencies`.
|
|
|
|
By default poetry installs the `dev-dependencies` when you run this command :
|
|
```bash
|
|
poetry install
|
|
```
|
|
|
|
If you dont want to install the `dev-dependencies` you will have to add the option `--no-dev` like this :
|
|
```bash
|
|
poetry install --no-dev
|
|
```
|
|
|
|
## 2.2. How to run `tests` : by `module` ?
|
|
|
|
You can run tests on a specific package/module by specifying the path of this package/module, like this :
|
|
|
|
```bash
|
|
pytest tests/gamestonk_terminal/some_package
|
|
pytest tests/gamestonk_terminal/some_package/some_module.py
|
|
```
|
|
|
|
## 2.3. How to run `tests` : by `name` ?
|
|
|
|
You can run tests by their name :
|
|
|
|
```
|
|
pytest -k "test_function1"
|
|
```
|
|
|
|
|
|
## 2.4. How to run `tests` : by `markers` ?
|
|
|
|
You can run tests only on specific markers, like this :
|
|
|
|
```
|
|
pytest -m slow
|
|
pytest -k "not slow"
|
|
pytest -k "not slow and not network"
|
|
```
|
|
|
|
You can list the available markers using this command :
|
|
|
|
```
|
|
pytest --markers
|
|
```
|
|
|
|
## 2.5. How to skip tests function ?
|
|
|
|
```python
|
|
import pytest
|
|
|
|
@pytest.skip
|
|
def test_some_function(mocker):
|
|
pass
|
|
|
|
@pytest.skip("This time with a comment")
|
|
def test_another_function(mocker):
|
|
pass
|
|
```
|
|
|
|
## 2.6. How to skip tests modules ?
|
|
|
|
```python
|
|
import pytest
|
|
|
|
pytest.skip(msg="Some optional comment.", allow_module_level=True)
|
|
|
|
def test_some_function(mocker):
|
|
pass
|
|
```
|
|
|
|
# 3. Build `unit tests`
|
|
|
|
## 3.1. Where to add my `test module` ?
|
|
|
|
All the `unit tests` should be insides the `tests` folder.
|
|
|
|
There should be at most one `test module` for each `module` of `Gamestonk Terminal`.
|
|
|
|
Each `test module` should follow the same path than the `module` it is `testing`.
|
|
|
|
For instance to `test` the following module :
|
|
- `gamestonk_terminal/stocks/due_diligence/dd_controller.py`
|
|
|
|
A `test module` should be added here :
|
|
- `tests/gamestonk_terminal/stocks/due_diligence/test_dd_controller.py`
|
|
|
|
## 3.2. How to record network ?
|
|
|
|
Network exchange can be recording inside a `test function` using the `@pytest.mark.vcr` `marker`.
|
|
|
|
Here is an example :
|
|
|
|
```python
|
|
@pytest.mark.vcr
|
|
def test_single():
|
|
assert requests.get("http://httpbin.org/get").text == '{"get": true}'
|
|
```
|
|
|
|
You can choose the `cassette` name using the `@pytest.mark.default_cassette` marker.
|
|
Here is an example :
|
|
|
|
```python
|
|
@pytest.mark.default_cassette("example.yaml")
|
|
@pytest.mark.vcr
|
|
def test_default():
|
|
assert requests.get("http://httpbin.org/get").text == '{"get": true}'
|
|
```
|
|
|
|
## 3.2. Where to put data files ?
|
|
|
|
Each data file should be related exactly to one `test module`.
|
|
|
|
The purpose is to have each `test module` as self-contained as possible.
|
|
|
|
Data related to a `test module` should be stored within the same folder than this `test module`.
|
|
|
|
Example with `tests/gamestonk_terminal/some_package/some_module.py` :
|
|
|
|
- `tests/gamestonk_terminal/some_package`/cassettes/`some_module`/test_function1.yaml
|
|
- `tests/gamestonk_terminal/some_package`/txt/`some_module`/test_function1.csv
|
|
- `tests/gamestonk_terminal/some_package`/csv/`some_module`/test_function1.csv
|
|
- `tests/gamestonk_terminal/some_package`/json/`some_module`/test_function1.json
|
|
|
|
## 3.3. How to filter `api keys` on `cassettes` ?
|
|
|
|
The filtering of `api keys` from a `cassette` should be done directly inside the `test module` using this `cassette`.
|
|
|
|
The purpose is to have each `test module` as self-contained as possible.
|
|
|
|
To filter `api keys` from a `cassettes` you can define a `vcr_config` fixture inside your `test module`.
|
|
|
|
Here is an example of filtering :
|
|
|
|
```python
|
|
@pytest.fixture(scope="module")
|
|
def vcr_config():
|
|
return {
|
|
"filter_query_parameters": [
|
|
("token", "MOCK_TOKEN"),
|
|
]
|
|
}
|
|
```
|
|
|
|
More information about `vcr_config` fixture in the documentation of `pytest-recording` :
|
|
- https://github.com/kiwicom/pytest-recording
|
|
|
|
## 3.4. How to `mock` ?
|
|
|
|
**PYTEST-MOCK**
|
|
|
|
A `mocker` fixture is available through the package `pytest-mock`.
|
|
|
|
Here is an example on how to `mocker` fixture can be used :
|
|
```python
|
|
def test_something(mocker):
|
|
mocker.patch("some_package.some_module")
|
|
```
|
|
|
|
More information about `pytest-mock` are available here :
|
|
- https://pypi.org/project/pytest-mock/
|
|
|
|
**MOCKEYPATCH**
|
|
|
|
There is also a `monkeypatch` fixture available by default inside `pytest`.
|
|
|
|
```python
|
|
def test_double(monkeypatch):
|
|
monkeypatch.setattr("some_package.some_module", "Some value")
|
|
```
|
|
|
|
More information about `monkeypatch` are available here :
|
|
- https://docs.pytest.org/en/6.2.x/monkeypatch.html
|
|
|
|
## 3.5. Which `helpers` are available ?
|
|
|
|
You can find the available helpers inside the following package/module :
|
|
- `tests/helpers/`
|
|
- `tests/conftest.py`
|
|
|
|
See also the `pytest fixtures` which are autoloaded helpers.
|
|
|
|
## 3.6. Which `fixtures` are available ?
|
|
|
|
You can list all the available `pytest fixtures` using the following command :
|
|
|
|
```
|
|
pytest --fixtures
|
|
```
|
|
|
|
More on custom fixtures here :
|
|
- [FIXTURES](FIXTURES.md)
|
|
|
|
## 3.7. Which `markers` are available ?
|
|
|
|
You can list the available markers using this command :
|
|
|
|
```
|
|
pytest --markers
|
|
```
|
|
|
|
More information on markers location are available in pytest documentation :
|
|
- https://docs.pytest.org/en/6.2.x/mark.html#mark
|
|
|
|
|
|
## 3.8. Known `issue` / `solution`
|
|
|
|
**YFINANCE**
|
|
|
|
If a method is using the `yfinance` library but do not let you pick the `start/end` dates it will pick the current date each time.
|
|
|
|
So your test might fail the next days.
|
|
|
|
To solve that you can filter the `start/end` date like this :
|
|
|
|
```python
|
|
@pytest.fixture(scope="module")
|
|
def vcr_config():
|
|
return {
|
|
"filter_query_parameters": [
|
|
("period1", "1598220000"),
|
|
("period2", "1635980400"),
|
|
]
|
|
}
|
|
```
|
|
|
|
You can also refactor this method to let access to `stard/end` dates.
|
|
|
|
|
|
**USER-AGENT**
|
|
|
|
Some function uses a random `User-Agent` on the `HTTP HEADER` when fetching data from an `API`.
|
|
|
|
Here is how to filter this random `User-Agent`.
|
|
|
|
```python
|
|
@pytest.fixture(scope="module")
|
|
def vcr_config():
|
|
return {
|
|
"filter_headers": [("User-Agent", None)],
|
|
}
|
|
```
|
|
|
|
**YFINANCE**
|
|
|
|
If you do something like this with `yfinance` library.
|
|
|
|
```python
|
|
import yfinance as yf
|
|
|
|
yf.download(tickers="VCYT QSI")
|
|
```
|
|
|
|
Chances are you requests will be multi-threaded.
|
|
|
|
Issues : as for now `vcrpy` seems to be incompatible with multi-threading.
|
|
|
|
The library `vcrpy` is used to record `cassettes` (`network` calls into `yaml` files).
|
|
|
|
Here is a solution to still combine `yfinance` and `vcrpy` :
|
|
```python
|
|
import pytest
|
|
import yfinance
|
|
|
|
yf_download = yf.download
|
|
def mock_yf_download(*args, **kwargs):
|
|
kwargs["threads"] = False
|
|
return yf_download(*args, **kwargs)
|
|
|
|
@pytest.mark.vcr
|
|
def test_ark_orders_view(kwargs_dict, mocker, use_color):
|
|
mocker.patch("yfinance.download", side_effect=mock_yf_download)
|
|
|
|
yf.download(tickers="VCYT QSI")
|
|
```
|
|
|
|
## 3.9. List of useful `vscode` tools for `unit tests`
|
|
|
|
**VSCODE TESTING**
|
|
|
|
The default testing tool from `vscode` should let you add breakpoints and run debug on a specific `test`.
|
|
|
|
It's a convenient way to see what's inside your `test` while running.
|
|
|
|
More information on this tool are available here :
|
|
- https://code.visualstudio.com/docs/python/testing
|
|
|
|
## 3.10. How to handle `dev-dependencies` ?
|
|
|
|
**UPDATE PYPROJECT**
|
|
|
|
If a library need to be added or removed from `dev-dependencies` this can be done directly in the `pyproject.toml` file.
|
|
|
|
All the `dev-dependencies` should be under the `tool.poetry.dev-dependencies` section, like in this example :
|
|
|
|
```toml
|
|
[tool.poetry.dev-dependencies]
|
|
pytest = "^6.2.2"
|
|
pylint = "^2.7.2"
|
|
flake8 = "^3.9.0"
|
|
...
|
|
```
|
|
|
|
**EXPORT REQUIREMENTS**
|
|
|
|
After updating the `pyproject.toml` you will have to export the `requirements` files using the following commands :
|
|
```bash
|
|
poetry export -f requirements.txt -o requirements.txt --without-hashes --dev
|
|
poetry export -f requirements.txt -o requirements-full.txt --extras prediction --without-hashes --dev
|
|
```
|
|
|
|
# 4. Maintain `unit tests`
|
|
|
|
## 4.1. What is the PR process ? (in progress)
|
|
Here are the steps to write proper tests for Gamestonk :
|
|
|
|
A. Find right place
|
|
B. Verify coverage is above 90%
|
|
C. Set the right markers
|
|
|
|
**A. Find right place**
|
|
|
|
Put the code following the same module and package structure than `gamestonk_terminal` package.
|
|
|
|
**B. Verify coverage is above 90%**
|
|
|
|
Once you made your `PullRequest` an automation will let you know whether or not you have the proper amount of tests coverage.
|
|
|
|
You can also run the following command to check your coverage manually :
|
|
- `pytest --cov --cov-fail-under=90`
|
|
|
|
**C. Set the right markers**
|
|
|
|
If parts of your have specificities like :
|
|
- being `slow`
|
|
- requiring `network` connectivity
|
|
- requiring `authentication` to `APIs`
|
|
|
|
Then the proper markers should be sent on each test.
|
|
|
|
|
|
The rests of this document is there to provide a deeper comprehension of this steps.
|
|
|
|
A tests update might be asked whenever the answer to one of the following question is `yes` :
|
|
|
|
A. Is this PR fixing bug which was not detected by the current tests ?
|
|
B. Is this PR reducing code coverage ?
|
|
C. Is this PR adding features ?
|
|
D. Is this PR updating features ?
|
|
|
|
## 4.2. Which automations are available ?
|
|
|
|
**PULL REQUEST**
|
|
|
|
A `github action` should comment every `Pull Request` with the code `coverage`.
|
|
|
|
This automation will not enforce any rules regarding `tests`.
|
|
|
|
## 4.3. How to check code coverage ?
|
|
|
|
**PULL REQUEST AUTOMATION**
|
|
|
|
You should be able to see the code coverage on comment of your `Pull Requests`.
|
|
|
|
**MANUALLY**
|
|
|
|
This is how to manually check code coverage.
|
|
```bash
|
|
pytest --cov=gamestonk_terminal --cov=terminal --cov-report term-missing
|
|
```
|
|
|
|
You can also select a specific package/module with the option `--cov`.
|
|
|
|
Here is an example where we select only the package `stocks\due_diligence` :
|
|
```bash
|
|
pytest --cov=gamestonk_terminal\stocks\due_diligence --cov-report term-missing
|
|
```
|
|
|
|
**PRE-COMMIT**
|
|
|
|
Code `coverage` and `unit tests` are not run on `pre-commit` hooks since it will slow down each commit.
|