Skip to content

Refactor and overhaul the test suite #3426

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 83 commits into from
Feb 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
fc5aea4
Split tests based on whether they make a request
harshil21 Dec 8, 2022
77ecda1
Override get_me in tests to return cached User() object
harshil21 Dec 9, 2022
f1207dc
move testing of initialization of bot to Req part
harshil21 Dec 9, 2022
4fe9955
use TEST_WITH_OPT_DEPS to justify removing TestBot.localize
harshil21 Dec 10, 2022
c67f100
import `TEST_WITH_OPT_DEPS` from conftest instead of redefining every…
harshil21 Dec 10, 2022
7b412cf
use context manager for data_file in fixtures
harshil21 Dec 11, 2022
8725e4b
rearrange the order of one test method (so tests pass)
harshil21 Dec 11, 2022
0920f95
Optimize `TestApplication` significantly by making small changes
harshil21 Dec 12, 2022
256758d
remove pytest.mark.timeout from requirements
harshil21 Dec 12, 2022
e2cc536
Fix: 2 app & job tests were not a coroutine function
harshil21 Dec 12, 2022
0a7d03b
don't close event loop after session end
harshil21 Dec 12, 2022
40ad364
use DictExtBot in more places, where appropriate.
harshil21 Dec 13, 2022
fb550d1
rearrange test methods in TestBot again
harshil21 Dec 13, 2022
58aaf04
Don't rerun tests if they have xfailed
harshil21 Dec 13, 2022
b46a4bb
Make the bot_methods decorator not return camelCase names by default
harshil21 Dec 13, 2022
dea47cf
small optimization: Make test_coroutine_functions use inspect.iscorou…
harshil21 Dec 13, 2022
857949a
Feat: Make some tests run concurrently via asyncio.gather/as_complete…
harshil21 Dec 14, 2022
6f40363
Fix/Refactor: Move test_custom_emoji_sticker to TestStickerReq
harshil21 Dec 14, 2022
e90f0a0
Miscellaneous fixes
harshil21 Dec 14, 2022
b330c97
small refactor: delete a duplicate test, split one test
harshil21 Dec 14, 2022
738fd44
refactor and rearrange: remove bot.delete_webhook() from arbitrary ca…
harshil21 Dec 14, 2022
77b8438
rearrange tests
harshil21 Dec 15, 2022
664c103
Adding two missing slot_behaviour tests in test_sticker.py
harshil21 Dec 15, 2022
b2e292e
small refactor: use a for loop, fixture, and add another assert
harshil21 Dec 15, 2022
cc1e45e
delete TestUserNoReq::test_de_json_without_username
harshil21 Dec 15, 2022
6ea3515
Add a README for tests and add some comments to requirements-dev.txt
harshil21 Dec 15, 2022
4b55ee5
hopefully fix tests on GH CI
harshil21 Dec 16, 2022
b3e9e8d
Hopefully fix tests on py3.10+
harshil21 Dec 17, 2022
4ea871e
Hotfix to make the tests run
harshil21 Dec 17, 2022
4f69b52
add a flaky marker to test_signal_handler
harshil21 Dec 17, 2022
c8fab41
Update keys for bot data
Bibo-Joshi Dec 18, 2022
8fa504f
remove env var fetching of bots
harshil21 Dec 18, 2022
7f5996e
Experiment: Try making Job Queue tests more stable
harshil21 Dec 19, 2022
1baebf2
Experiment 1 cont: Attempt to stablize job queue tests on macos
harshil21 Dec 20, 2022
a608e48
Experiment 1 cont: stablilize another test on macos
harshil21 Dec 20, 2022
67b2ba2
Experiment 1 end: use asyncio.wait_for so tests cannot hang
harshil21 Dec 20, 2022
5c778d1
Experiment 2: Make GH CI use 10 workers with loadgroup
harshil21 Dec 20, 2022
b5c5d16
merge master and fix conflicts
harshil21 Jan 3, 2023
050f650
mark job queue tests as flaky
harshil21 Jan 3, 2023
d7c09a1
attempt to fix potential race condition in test_passport.py
harshil21 Jan 3, 2023
e37ab7e
attempt to fix the error: 'free variable input_video referenced befor…
harshil21 Jan 3, 2023
3bdd981
fix error while collecting tests in test_datetime.py
harshil21 Jan 3, 2023
402aa03
why tests no run?
harshil21 Jan 3, 2023
024315b
change scope of real_topic back to function
harshil21 Jan 3, 2023
f3c671d
Experiment 2 end: Revert back to -n auto, but keep --dist=loadgroup
harshil21 Jan 3, 2023
dc847df
Experiment 3: Run tests of optional dependencies in one go
harshil21 Jan 3, 2023
744308d
run getUpdates and webhook tests in same worker
harshil21 Jan 3, 2023
72e0a6e
Experiment 3 cont: make the variable in a single line
harshil21 Jan 3, 2023
ebad4e4
increase sleep duration in test_conversation_timout
harshil21 Jan 3, 2023
a9dcdae
merge master and fix conflict
harshil21 Jan 4, 2023
2510df8
fix potential race condition while unpinning message
harshil21 Jan 4, 2023
8c3296b
merge master and fix conflicts'
harshil21 Jan 12, 2023
03501e4
make test_get_chat_arbitrary_callback_data use channel_id for testing
harshil21 Jan 12, 2023
891ed29
Address review of fc5aea4: Use `TestXXXWith(out)Request`
harshil21 Jan 12, 2023
6aa539f
make tz_bot fixture use default_bots dictionary as cache instead
harshil21 Jan 12, 2023
0588363
Address review of fc5aea4: Change Space to suggested name instead
harshil21 Jan 12, 2023
e079c6b
Address review of fc5aea4: Add docstring to TestBotWithoutRequest
harshil21 Jan 12, 2023
53031be
Address review of 77ecda1: Changes in test_bot.py and conftest.py
harshil21 Jan 12, 2023
2596d4c
Address review of 4fe9955: Change skipif reason
harshil21 Jan 13, 2023
ab3be7e
Address review of 0920f95: Rename the bot used in app tests
harshil21 Jan 13, 2023
ebecf27
fix some typos and tiny bugs in test_app.py
harshil21 Jan 13, 2023
e60d2e1
Address review of 857949a: slightly modify test logic
harshil21 Jan 13, 2023
01c8b5c
Address review of b330c97: change variable name i -> key
harshil21 Jan 13, 2023
6a9bbc0
Address review of 6ea3515: Update test readme and add to index.html
harshil21 Jan 13, 2023
119858d
Address review of 4b55ee5: Drop testing of file_id with get_file
harshil21 Jan 13, 2023
a9ce337
Address review of 4ea871e: put variable in class instead of global
harshil21 Jan 13, 2023
7d64045
Address review of 4f69b52: Modify comment in test_signal_handlers
harshil21 Jan 13, 2023
f65efd6
merge master and fix conflicts
harshil21 Feb 5, 2023
6acd324
add CSI comment about shutting down bots
harshil21 Feb 5, 2023
ac14ae1
add back test_flag as autouse function
harshil21 Feb 5, 2023
eb8de60
add except TelegramError to an app test
harshil21 Feb 5, 2023
5d6a0c7
fix link in contributing.rst
harshil21 Feb 5, 2023
2587d97
expand comment in test.yml
harshil21 Feb 5, 2023
3da231f
move contributing and testing below coc
harshil21 Feb 5, 2023
7a79ae4
make BotInfo a proper class
harshil21 Feb 5, 2023
ac401b2
self -> bot
harshil21 Feb 5, 2023
e6eec74
move one_time_bot from application.py to conftest
harshil21 Feb 5, 2023
38ca987
add CSI comment about datetime test parametrization
harshil21 Feb 5, 2023
5b45a02
revert Experiment 1
harshil21 Feb 5, 2023
a3c4b03
add csi comment in get_bot_user
harshil21 Feb 5, 2023
4d29721
Revert Experiment 2: tests are a bit too flaky
harshil21 Feb 8, 2023
018d82b
merge master, fix conflicts and apply new changes
harshil21 Feb 8, 2023
45a092a
merge master and fix conflict yet again
harshil21 Feb 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 4 additions & 26 deletions .github/CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,35 +84,13 @@ Here's how to make a one-off code change.

- In addition, PTB uses some formatting/styling and linting tools in the pre-commit setup. Some of those tools also have command line tools that can help to run these tools outside of the pre-commit step. If you'd like to leverage that, please have a look at the `pre-commit config file`_ for an overview of which tools (and which versions of them) are used. For example, we use `Black`_ for code formatting. Plugins for Black exist for some `popular editors`_. You can use those instead of manually formatting everything.

- Please ensure that the code you write is well-tested.
- Please ensure that the code you write is well-tested and that all automated tests still pass. We
have dedicated an `testing page`_ to help you with that.

- In addition to that, we provide the `dev` marker for pytest. If you write one or multiple tests and want to run only those, you can decorate them via `@pytest.mark.dev` and then run it with minimal overhead with `pytest ./path/to/test_file.py -m dev`.

- Don’t break backward compatibility.
- Don't break backward compatibility.

- Add yourself to the AUTHORS.rst_ file in an alphabetical fashion.

- Before making a commit ensure that all automated tests still pass:

.. code-block:: bash

$ pytest -v

Since the tests can take a while to run, you can speed things up by running them in parallel
using `pytest-xdist`_ (note that this may effect the result of the test in some rare cases):

.. code-block:: bash

$ pytest -v -n auto --dist=loadfile

To run ``test_official`` (particularly useful if you made API changes), run

.. code-block:: bash

$ export TEST_OFFICIAL=true

prior to running the tests.

- If you want run style & type checks before committing run

.. code-block:: bash
Expand Down Expand Up @@ -287,4 +265,4 @@ break the API classes. For example:
.. _`RTD build`: https://docs.python-telegram-bot.org/en/doc-fixes
.. _`CSI`: https://standards.mousepawmedia.com/en/stable/csi.html
.. _`section`: #documenting
.. _`pytest-xdist`: https://github.com/pytest-dev/pytest-xdist
.. _`testing page`: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/tests/README.rst
41 changes: 11 additions & 30 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,42 +44,23 @@ jobs:
# The first & second one are achieved by mocking the corresponding import
# See test_helpers.py & test_no_passport.py for details
run: |
# Test without passport
pytest -v --cov -k test_no_passport.py
# We test without optional dependencies first. This includes:
# - without pytz
# - without jobqueue
# - without ratelimiter
# - without webhooks
# - without arbitrary callback data
# - without socks suppport
TO_TEST="test_no_passport.py or test_datetime.py or test_defaults.py or test_jobqueue.py or test_applicationbuilder.py or test_ratelimiter.py or test_updater.py or test_callbackdatacache.py or test_request.py"
pytest -v --cov -k "${TO_TEST}"
status=$?

# test without pytz
pytest -v --cov --cov-append -k test_datetime.py
status=$(( $? > status ? $? : status))
pytest -v --cov --cov-append -k test_defaults.py
status=$(( $? > status ? $? : status))

# test without pytz & jobqueue
pytest -v --cov --cov-append -k test_jobqueue.py
pytest -v --cov --cov-append -k test_applicationbuilder.py
status=$(( $? > status ? $? : status))

# Test without ratelimiter
pytest -v --cov --cov-append -k test_ratelimiter.py
status=$(( $? > status ? $? : status))

# Test without webhooks
pytest -v --cov --cov-append -k test_updater.py
status=$(( $? > status ? $? : status))

# Test without callback-data
pytest -v --cov --cov-append -k test_callbackdatacache.py
status=$(( $? > status ? $? : status))

# Test without socks
pytest -v --cov --cov-append -k test_request.py
status=$(( $? > status ? $? : status))

# Test the rest
export TEST_WITH_OPT_DEPS='true'
pip install -r requirements-opts.txt
# `-n auto --dist loadfile` uses pytest-xdist to run each test file on a different CPU
# worker
# worker. Increasing number of workers has little effect on test duration, but it seems
# to increase flakyness, specially on python 3.7 with --dist=loadgroup.
pytest -v --cov --cov-append -n auto --dist loadfile
status=$(( $? > status ? $? : status))
exit ${status}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/type_completeness.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- name: Compare Completeness
uses: jannekem/run-python-script-action@v1
with:
script: |
script: |
import json
import os
from pathlib import Path
Expand Down
5 changes: 2 additions & 3 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
GitHub Repository <https://github.com/python-telegram-bot/python-telegram-bot/>
Telegram Channel <https://t.me/pythontelegrambotchannel/>
Telegram User Group <https://t.me/pythontelegrambotgroup/>
contributing
coc


contributing
testing
1 change: 1 addition & 0 deletions docs/source/testing.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.. include:: ../../tests/README.rst
8 changes: 4 additions & 4 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
pre-commit
pre-commit # needed for pre-commit hooks in the git commit command

# For the test suite
pytest==7.2.1
pytest-asyncio==0.20.3
pytest-timeout==2.1.0 # used to timeout tests
pytest-asyncio==0.20.3 # needed because pytest doesn't come with native support for coroutines as tests
pytest-xdist==3.1.0 # xdist runs tests in parallel

flaky # Used for flaky tests (flaky decorator)
beautifulsoup4 # used in test_official for parsing tg docs

wheel # required for building the wheels for releases
5 changes: 4 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ filterwarnings =
; Unfortunately due to https://github.com/pytest-dev/pytest/issues/8343 we can't have this here
; and instead do a trick directly in tests/conftest.py
; ignore::telegram.utils.deprecate.TelegramDeprecationWarning
markers = dev: If you want to test a specific test, use this
markers =
dev: If you want to test a specific test, use this
no_req
req
asyncio_mode = auto

[coverage:run]
Expand Down
101 changes: 101 additions & 0 deletions tests/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
==============
Testing in PTB
==============

PTB uses `pytest`_ for testing. To run the tests, you need to
have pytest installed along with a few other dependencies. You can find the list of dependencies
in the ``requirements-dev.txt`` file in the root of the repository.

Running tests
=============

To run the entire test suite, you can use the following command:

.. code-block:: bash

$ pytest

This will run all the tests, including the ones which make a request to the Telegram servers, which
may take a long time (total > 13 mins). To run only the tests that don't require a connection, you
can run the following command:

.. code-block:: bash

$ pytest -m no_req

Or alternatively, you can run the following command to run only the tests that require a connection:

.. code-block:: bash

$ pytest -m req

To further speed up the tests, you can run them in parallel using the ``-n`` flag (requires `pytest-xdist`_). But beware that
this will use multiple CPU cores on your machine. The ``--dist`` flag is used to specify how the
tests will be distributed across the cores. The ``loadgroup`` option is used to distribute the tests
such that tests marked with ``@pytest.mark.xdist_group("name")`` are run on the same core — important if you want avoid race conditions in some tests:

.. code-block:: bash

$ pytest -n auto --dist=loadgroup

This will result in a significant speedup, but may cause some tests to fail. If you want to run
the failed tests in isolation, you can use the ``--lf`` flag:

.. code-block:: bash

$ pytest --lf


Writing tests
=============

PTB has a separate test file for every file in the ``telegram.*`` namespace. Further, the tests for
the ``telegram`` module are split into two classes, based on whether the test methods in them make a
request or not. When writing tests, make sure to split them into these two classes, and make sure
to name the test class as: ``TestXXXWithoutRequest`` for tests that don't make a request, and ``TestXXXWithRequest`` for tests that do.

Writing tests is a creative process; allowing you to design your test however you'd like, but there
are a few conventions that you should follow:

- Each new test class needs a ``test_slot_behaviour``, ``test_to_dict``, ``test_de_json`` and
``test_equality`` (in most cases).

- Make use of pytest's fixtures and parametrize wherever possible. Having knowledge of pytest's
tooling can help you as well. You can look at the existing tests for examples and inspiration.

If you have made some API changes, you may want to run ``test_official`` to validate that the changes are
complete and correct. To run it, export an environment variable first:

.. code-block:: bash

$ export TEST_OFFICIAL=true

and then run ``pytest tests/test_official.py``.

We also have another marker, ``@pytest.mark.dev``, which you can add to tests that you want to run selectively.
Use as follows:

.. code-block:: bash

$ pytest -m dev


Bots used in tests
==================

If you run the tests locally, the test setup will use one of the two public bots available. Which
bot of the two gets chosen for the test session is random. Whereas when the tests on the
Github Actions CI are run, the test setup allocates a different, but same bot for every combination of Python version and
OS.

Thus, number of bots used for testing locally is 2 (called as fallback bots), and on the CI,
its [3.7, 3.8, 3.9, 3.10, 3.11] x [ubuntu-latest, macos-latest, windows-latest] = 15. Bringing the
total number of bots used for testing to 17.


That's it! If you have any questions, feel free to ask them in the `PTB dev
group`_.

.. _pytest: https://docs.pytest.org/en/stable/
.. _pytest-xdist: https://pypi.org/project/pytest-xdist/
.. _PTB dev group: https://t.me/pythontelegrambotgroup
27 changes: 14 additions & 13 deletions tests/bots.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,26 +45,27 @@
BOTS = json.loads(base64.b64decode(BOTS).decode("utf-8"))
JOB_INDEX = int(JOB_INDEX)

FALLBACKS = json.loads(base64.b64decode(FALLBACKS).decode("utf-8"))
FALLBACKS = json.loads(base64.b64decode(FALLBACKS).decode("utf-8")) # type: list[dict[str, str]]


def get(name, fallback):
# If we have TOKEN, PAYMENT_PROVIDER_TOKEN, CHAT_ID, SUPER_GROUP_ID,
# CHANNEL_ID, BOT_NAME, or BOT_USERNAME in the environment, then use that
val = os.getenv(name.upper())
if val:
return val
class BotInfoProvider:
def __init__(self):
self._cached = {}

def get_info(self):
if self._cached:
return self._cached
self._cached = {k: get(k, v) for k, v in random.choice(FALLBACKS).items()}
return self._cached


def get(key, fallback):
# If we're running as a github action then fetch bots from the repo secrets
if GITHUB_ACTION is not None and BOTS is not None and JOB_INDEX is not None:
try:
return BOTS[JOB_INDEX][name]
except (KeyError, IndexError):
return BOTS[JOB_INDEX][key]
except (IndexError, KeyError):
pass

# Otherwise go with the fallback
return fallback


def get_bot():
return {k: get(k, v) for k, v in random.choice(FALLBACKS).items()}
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy