diff options
author | Jordan Cook <jordan.cook@pioneer.com> | 2021-04-22 00:02:08 -0500 |
---|---|---|
committer | Jordan Cook <jordan.cook@pioneer.com> | 2021-04-22 00:21:49 -0500 |
commit | 3ec85f0107caedbe3b3bd8080bd3dac81b7baa17 (patch) | |
tree | fb617be7c7760390b7239899e6522d865956342f | |
parent | b1070a950e0365085b7edf8ef2e4a65c77630ac9 (diff) | |
download | requests-cache-3ec85f0107caedbe3b3bd8080bd3dac81b7baa17.tar.gz |
Turn multi-threaded stress tests into test (sub)classes
This is mainly to take advantage of fail-fast connection tests; otherwise, these tests may just
hang if backend dependenices are installed but backend services are not set up.
See issue #221 for details.
-rw-r--r-- | tests/integration/test_backends.py | 55 | ||||
-rw-r--r-- | tests/integration/test_dynamodb.py | 9 | ||||
-rw-r--r-- | tests/integration/test_filesystem.py | 9 | ||||
-rw-r--r-- | tests/integration/test_gridfs.py | 8 | ||||
-rw-r--r-- | tests/integration/test_mongodb.py | 8 | ||||
-rw-r--r-- | tests/integration/test_redis.py | 6 | ||||
-rw-r--r-- | tests/integration/test_sqlite.py | 20 | ||||
-rw-r--r-- | tests/integration/test_thread_safety.py | 35 |
8 files changed, 100 insertions, 50 deletions
diff --git a/tests/integration/test_backends.py b/tests/integration/test_backends.py index 13f5424..9849a6b 100644 --- a/tests/integration/test_backends.py +++ b/tests/integration/test_backends.py @@ -1,8 +1,11 @@ import pytest +from threading import Thread +from time import time from typing import Dict, Type -from requests_cache.backends.base import BaseStorage -from tests.conftest import CACHE_NAME +from requests_cache.backends.base import BaseCache, BaseStorage +from requests_cache.session import CachedSession +from tests.conftest import CACHE_NAME, N_ITERATIONS, N_THREADS, httpbin class BaseStorageTest: @@ -109,7 +112,55 @@ class BaseStorageTest: cache_2['key_2'] = 2 assert cache_1 == cache_2 + def test_str(self): + """Not much to test for __str__ methods, just make sure they return keys in some format""" + cache = self.init_cache() + for i in range(10): + cache[f'key_{i}'] = f'value_{i}' + for i in range(10): + assert f'key_{i}' in str(cache) + class Picklable: attr_1 = 'value_1' attr_2 = 'value_2' + + +class BaseCacheTest: + """Base class for testing cache backend classes""" + + backend_class: Type[BaseCache] = None + init_kwargs: Dict = {} + + def init_backend(self, clear=True, **kwargs): + kwargs['suppress_warnings'] = True + backend = self.backend_class(CACHE_NAME, **self.init_kwargs, **kwargs) + if clear: + backend.redirects.clear() + backend.responses.clear() + return backend + + @pytest.mark.parametrize('iteration', range(N_ITERATIONS)) + def test_caching_with_threads(self, iteration): + """Run a multi-threaded stress test for each backend""" + start = time() + session = CachedSession(backend=self.init_backend()) + url = httpbin('anything') + + def send_requests(): + for i in range(N_ITERATIONS): + session.get(url, params={f'key_{i}': f'value_{i}'}) + + threads = [Thread(target=send_requests) for i in range(N_THREADS)] + for t in threads: + t.start() + for t in threads: + t.join() + + elapsed = time() - start + average = (elapsed * 1000) / (N_ITERATIONS * N_THREADS) + print(f'{self.backend_class}: Ran {N_ITERATIONS} iterations with {N_THREADS} threads each in {elapsed} s') + print(f'Average time per request: {average} ms') + + for i in range(N_ITERATIONS): + assert session.cache.has_url(f'{url}?key_{i}=value_{i}') diff --git a/tests/integration/test_dynamodb.py b/tests/integration/test_dynamodb.py index 12da135..951d9c8 100644 --- a/tests/integration/test_dynamodb.py +++ b/tests/integration/test_dynamodb.py @@ -1,9 +1,9 @@ import pytest from unittest.mock import patch -from requests_cache.backends import DynamoDbDict +from requests_cache.backends import DynamoDbCache, DynamoDbDict from tests.conftest import AWS_OPTIONS, fail_if_no_connection -from tests.integration.test_backends import BaseStorageTest +from tests.integration.test_backends import BaseCacheTest, BaseStorageTest # Run this test module last, since the DynamoDB container takes the longest to initialize pytestmark = pytest.mark.order(-1) @@ -29,3 +29,8 @@ class TestDynamoDbDict(BaseStorageTest): """A spot check to make sure optional connection kwargs gets passed to connection""" DynamoDbDict('test', region_name='us-east-2', invalid_kwarg='???') mock_resource.assert_called_with('dynamodb', region_name='us-east-2') + + +class TestDynamoDbCache(BaseCacheTest): + backend_class = DynamoDbCache + init_kwargs = AWS_OPTIONS diff --git a/tests/integration/test_filesystem.py b/tests/integration/test_filesystem.py index 2191daa..0a3caec 100644 --- a/tests/integration/test_filesystem.py +++ b/tests/integration/test_filesystem.py @@ -1,8 +1,8 @@ from os.path import isfile from shutil import rmtree -from requests_cache.backends import FileDict -from tests.integration.test_backends import CACHE_NAME, BaseStorageTest +from requests_cache.backends import FileCache, FileDict +from tests.integration.test_backends import CACHE_NAME, BaseCacheTest, BaseStorageTest class TestFileDict(BaseStorageTest): @@ -26,3 +26,8 @@ class TestFileDict(BaseStorageTest): assert len(list(cache.paths())) == self.num_instances for path in cache.paths(): assert isfile(path) + + +class TestFileCache(BaseCacheTest): + backend_class = FileCache + init_kwargs = {'use_temp': True} diff --git a/tests/integration/test_gridfs.py b/tests/integration/test_gridfs.py index 4879957..d463824 100644 --- a/tests/integration/test_gridfs.py +++ b/tests/integration/test_gridfs.py @@ -3,9 +3,9 @@ from unittest.mock import patch from pymongo import MongoClient -from requests_cache.backends import GridFSPickleDict, get_valid_kwargs +from requests_cache.backends import GridFSCache, GridFSPickleDict, get_valid_kwargs from tests.conftest import fail_if_no_connection -from tests.integration.test_backends import BaseStorageTest +from tests.integration.test_backends import BaseCacheTest, BaseStorageTest @pytest.fixture(scope='module', autouse=True) @@ -33,3 +33,7 @@ class TestGridFSPickleDict(BaseStorageTest): """A spot check to make sure optional connection kwargs gets passed to connection""" GridFSPickleDict('test', host='http://0.0.0.0', port=1234, invalid_kwarg='???') mock_client.assert_called_with(host='http://0.0.0.0', port=1234) + + +class TestGridFSCache(BaseCacheTest): + backend_class = GridFSCache diff --git a/tests/integration/test_mongodb.py b/tests/integration/test_mongodb.py index 52c8935..a186ee7 100644 --- a/tests/integration/test_mongodb.py +++ b/tests/integration/test_mongodb.py @@ -3,9 +3,9 @@ from unittest.mock import patch from pymongo import MongoClient -from requests_cache.backends import MongoDict, MongoPickleDict, get_valid_kwargs +from requests_cache.backends import MongoCache, MongoDict, MongoPickleDict, get_valid_kwargs from tests.conftest import fail_if_no_connection -from tests.integration.test_backends import BaseStorageTest +from tests.integration.test_backends import BaseCacheTest, BaseStorageTest @pytest.fixture(scope='module', autouse=True) @@ -35,3 +35,7 @@ class TestMongoPickleDict(BaseStorageTest): """A spot check to make sure optional connection kwargs gets passed to connection""" MongoDict('test', host='http://0.0.0.0', port=1234, invalid_kwarg='???') mock_client.assert_called_with(host='http://0.0.0.0', port=1234) + + +class TestMongoCache(BaseCacheTest): + backend_class = MongoCache diff --git a/tests/integration/test_redis.py b/tests/integration/test_redis.py index 1c00ff1..899d2b6 100644 --- a/tests/integration/test_redis.py +++ b/tests/integration/test_redis.py @@ -3,7 +3,7 @@ from unittest.mock import patch from requests_cache.backends.redis import RedisCache, RedisDict from tests.conftest import fail_if_no_connection -from tests.integration.test_backends import BaseStorageTest +from tests.integration.test_backends import BaseCacheTest, BaseStorageTest @pytest.fixture(scope='module', autouse=True) @@ -24,3 +24,7 @@ class TestRedisDict(BaseStorageTest): """A spot check to make sure optional connection kwargs gets passed to connection""" RedisCache('test', username='user', password='pass', invalid_kwarg='???') mock_redis.assert_called_with(username='user', password='pass') + + +class TestRedisCache(BaseCacheTest): + backend_class = RedisCache diff --git a/tests/integration/test_sqlite.py b/tests/integration/test_sqlite.py index e93216d..63c67ec 100644 --- a/tests/integration/test_sqlite.py +++ b/tests/integration/test_sqlite.py @@ -2,8 +2,8 @@ import os from threading import Thread from unittest.mock import patch -from requests_cache.backends.sqlite import DbDict, DbPickleDict -from tests.integration.test_backends import CACHE_NAME, BaseStorageTest +from requests_cache.backends.sqlite import DbCache, DbDict, DbPickleDict +from tests.integration.test_backends import CACHE_NAME, BaseCacheTest, BaseStorageTest class SQLiteTestCase(BaseStorageTest): @@ -63,8 +63,8 @@ class SQLiteTestCase(BaseStorageTest): thread.join() # make sure connection is not closed by the thread - cache[0] = 0 - assert str(cache) == "{0: 0}" + cache['key_1'] = 'value_1' + assert list(cache.keys()) == ['key_1'] @patch('requests_cache.backends.sqlite.sqlite3') def test_connection_kwargs(self, mock_sqlite): @@ -80,3 +80,15 @@ class TestDbDict(SQLiteTestCase): class TestDbPickleDict(SQLiteTestCase): storage_class = DbPickleDict picklable = True + + +class TestDbCache(BaseCacheTest): + backend_class = DbCache + init_kwargs = {'use_temp': True} + + @classmethod + def teardown_class(cls): + try: + os.unlink(CACHE_NAME) + except Exception: + pass diff --git a/tests/integration/test_thread_safety.py b/tests/integration/test_thread_safety.py deleted file mode 100644 index bc99c72..0000000 --- a/tests/integration/test_thread_safety.py +++ /dev/null @@ -1,35 +0,0 @@ -import pytest -from threading import Thread -from time import time - -from requests_cache.backends import BACKEND_CLASSES -from requests_cache.session import CachedSession -from tests.conftest import AWS_OPTIONS, N_ITERATIONS, N_THREADS, httpbin - - -@pytest.mark.parametrize('iteration', range(N_ITERATIONS)) -@pytest.mark.parametrize('backend', BACKEND_CLASSES.keys()) -def test_caching_with_threads(backend, iteration): - """Run a multi-threaded stress test for each backend""" - start = time() - session = CachedSession(backend=backend, use_temp=True, **AWS_OPTIONS) - session.cache.clear() - url = httpbin('anything') - - def send_requests(): - for i in range(N_ITERATIONS): - session.get(url, params={f'key_{i}': f'value_{i}'}) - - threads = [Thread(target=send_requests) for i in range(N_THREADS)] - for t in threads: - t.start() - for t in threads: - t.join() - - elapsed = time() - start - average = (elapsed * 1000) / (N_ITERATIONS * N_THREADS) - print(f'{backend}: Ran {N_ITERATIONS} iterations with {N_THREADS} threads each in {elapsed} s') - print(f'Average time per request: {average} ms') - - for i in range(N_ITERATIONS): - assert session.cache.has_url(f'{url}?key_{i}=value_{i}') |