I want to test some async method that I use on an async view, but I want to be very clear that right now I am not testing my view, I already saw the documentation on django but it is only for views....
So I have this class containing multiple method and even contacting database asynchronously.
class SessionService:
""" Session service.
Attributes:
uuid (str): system uuid
gcloud (str): console gcloud_id
start (str): start time of the period
end (str): end time of the period
"""
def __init__(self, uuid: str, gcloud: str, start: str, end: str):
self.system_uuid: str = uuid
self.gcloud_id: str = gcloud
self.start: str = start
self.end: str = end
@database_sync_to_async
def __vision_data(self) -> Tuple[List[str], List[str], List[int]]:
""" Async method to get vision module's data from a uuid
Returns
----------
QuerySet
List where 1st element are gcloud id, 2nd UUID and last system IDs
"""
return System.objects.get_vision_conf_data(self.system_uuid)
@async_property
async def __vision_system_ids(self) -> List[int]:
""" get visions Ids
Returns:
List[int]
"""
data = await self.__vision_data()
return data[2]
async def get_session_list(self) -> List[Dict[str, Union[str, bool, int]]]:
""" Get all session for a period for a system
Returns:
List[Dict[str, Union[str, bool, int]]]
"""
spraying_session: SprayingSessionSumUpGetter = SprayingSessionSumUpGetter(self.gcloud_id, self.start,
self.end)
vision_systems_ids: List[int] = await self.__vision_system_ids
sessions: List[Dict[str, Union[str, pd.Timestamp]]] = await spraying_session.get_sum_up(
vision_systems_ids)
return sessions
I have two different situations, with the same problem at the end.
Situation ONE:
I am now trying to test get_session_list. To do so, I use TestCase from django.test .
My tests inherite from another class because I need to set a System instance and it depends on A LOT of other table so I manage it on another test Class SystemSetUpTest So, I have:
class SystemSetUpTest(TestCase):
@staticmethod
def setUp(): # pylint: disable=too-many-locals
create_everything_needed
@staticmethod
def tearDown():
delete_all_stuff
class SystemServicesTest(SystemSetUpTest):
def setUp(self):
super().setUp()
this is the test causing issue:
async def test_getting_sessions_list(self):
uuid: str = self.uuid
console_gcloud_id = 'console'
start_period = '2021-05-06'
end_period = '2021-05-07'
service = SessionService(uuid, console_gcloud_id, start_period, end_period)
print(await service.get_session_list())
and this is the complete Trace:
Traceback (most recent call last):
File "/app/systems/tests/test_set_up.py", line 60, in tearDown
User.objects.filter(email="jul@gmail.com").delete()
File "/usr/local/lib/python3.8/dist-packages/django/db/models/query.py", line 745, in delete
collector.collect(del_query)
File "/usr/local/lib/python3.8/dist-packages/django/db/models/deletion.py", line 243, in collect
new_objs = self.add(objs, source, nullable,
File "/usr/local/lib/python3.8/dist-packages/django/db/models/deletion.py", line 107, in add
if not objs:
File "/usr/local/lib/python3.8/dist-packages/django/db/models/query.py", line 284, in __bool__
self._fetch_all()
File "/usr/local/lib/python3.8/dist-packages/django/db/models/query.py", line 1324, in _fetch_all
self._result_cache = list(self._iterable_class(self))
File "/usr/local/lib/python3.8/dist-packages/django/db/models/query.py", line 51, in __iter__
results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
File "/usr/local/lib/python3.8/dist-packages/django/db/models/sql/compiler.py", line 1167, in execute_sql
cursor = self.connection.cursor()
File "/usr/local/lib/python3.8/dist-packages/django/utils/asyncio.py", line 26, in inner
return func(*args, **kwargs)
File "/usr/local/lib/python3.8/dist-packages/django/db/backends/base/base.py", line 259, in cursor
return self._cursor()
File "/usr/local/lib/python3.8/dist-packages/django/db/backends/base/base.py", line 237, in _cursor
return self._prepare_cursor(self.create_cursor(name))
File "/usr/local/lib/python3.8/dist-packages/django/db/utils.py", line 90, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/usr/local/lib/python3.8/dist-packages/django/db/backends/base/base.py", line 237, in _cursor
return self._prepare_cursor(self.create_cursor(name))
File "/usr/local/lib/python3.8/dist-packages/django/utils/asyncio.py", line 26, in inner
return func(*args, **kwargs)
File "/usr/local/lib/python3.8/dist-packages/django/db/backends/postgresql/base.py", line 236, in create_cursor
cursor = self.connection.cursor()
django.db.utils.InterfaceError: connection already closed
Basically it can tearDown the database, we can see it from:
File "/app/systems/tests/test_set_up.py", line 60, in tearDown User.objects.filter(email="jul@gmail.com").delete()
situation TWO class SystemServicesTest inherit direclty from TestCase and I set up my system directly in it's setUp method,
then the stack trace is:
Traceback (most recent call last):
File "/usr/local/lib/python3.8/dist-packages/asgiref/sync.py", line 223, in __call__
return call_result.result()
File "/usr/lib/python3.8/concurrent/futures/_base.py", line 437, in result
return self.__get_result()
File "/usr/lib/python3.8/concurrent/futures/_base.py", line 389, in __get_result
raise self._exception
File "/usr/local/lib/python3.8/dist-packages/asgiref/sync.py", line 292, in main_wrap
result = await self.awaitable(*args, **kwargs)
File "/app/systems/tests/test_services.py", line 41, in test_getting_sessions_list
print(await service.get_session_list())
File "/app/systems/services/session.py", line 85, in get_session_list
vision_systems_ids: List[int] = await self.__vision_system_ids
File "/usr/local/lib/python3.8/dist-packages/async_property/base.py", line 37, in get_value
return await self._fget(instance)
File "/app/systems/services/session.py", line 47, in __vision_system_ids
data = await self.__vision_data()
File "/usr/local/lib/python3.8/dist-packages/asgiref/sync.py", line 444, in __call__
ret = await asyncio.wait_for(future, timeout=None)
File "/usr/lib/python3.8/asyncio/tasks.py", line 455, in wait_for
return await fut
File "/usr/local/lib/python3.8/dist-packages/asgiref/current_thread_executor.py", line 22, in run
result = self.fn(*self.args, **self.kwargs)
File "/usr/local/lib/python3.8/dist-packages/channels/db.py", line 13, in thread_handler
return super().thread_handler(loop, *args, **kwargs)
File "/usr/local/lib/python3.8/dist-packages/asgiref/sync.py", line 486, in thread_handler
return func(*args, **kwargs)
File "/app/systems/services/session.py", line 38, in __vision_data
return System.objects.get_vision_conf_data(self.system_uuid)
File "/app/systems/models/manager/manager.py", line 130, in get_vision_conf_data
gcloud: List[str] = [data[0] for data in visions]
File "/usr/local/lib/python3.8/dist-packages/django/db/models/query.py", line 280, in __iter__
self._fetch_all()
File "/usr/local/lib/python3.8/dist-packages/django/db/models/query.py", line 1324, in _fetch_all
self._result_cache = list(self._iterable_class(self))
File "/usr/local/lib/python3.8/dist-packages/django/db/models/query.py", line 140, in __iter__
return compiler.results_iter(tuple_expected=True, chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
File "/usr/local/lib/python3.8/dist-packages/django/db/models/sql/compiler.py", line 1124, in results_iter
results = self.execute_sql(MULTI, chunked_fetch=chunked_fetch, chunk_size=chunk_size)
File "/usr/local/lib/python3.8/dist-packages/django/db/models/sql/compiler.py", line 1167, in execute_sql
cursor = self.connection.cursor()
File "/usr/local/lib/python3.8/dist-packages/django/utils/asyncio.py", line 26, in inner
return func(*args, **kwargs)
File "/usr/local/lib/python3.8/dist-packages/django/db/backends/base/base.py", line 259, in cursor
return self._cursor()
File "/usr/local/lib/python3.8/dist-packages/django/db/backends/base/base.py", line 237, in _cursor
return self._prepare_cursor(self.create_cursor(name))
File "/usr/local/lib/python3.8/dist-packages/django/db/utils.py", line 90, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/usr/local/lib/python3.8/dist-packages/django/db/backends/base/base.py", line 237, in _cursor
return self._prepare_cursor(self.create_cursor(name))
File "/usr/local/lib/python3.8/dist-packages/django/utils/asyncio.py", line 26, in inner
return func(*args, **kwargs)
File "/usr/local/lib/python3.8/dist-packages/django/db/backends/postgresql/base.py", line 236, in create_cursor
cursor = self.connection.cursor()
django.db.utils.InterfaceError: connection already closed
The problem seems to come from:
File "/app/systems/models/manager/manager.py", line 130, in get_vision_conf_data gcloud: List[str] = [data[0] for data in visions]
Here is the full method:
def get_vision_conf_data(self, system_uuid: str) -> Tuple[List[str], List[str], List[int]]:
""" Get vision conf data for a system
Parameters
----------
system_uuid : str
The system uuid
Returns
-------
Tuple[List[str], List[str], List[int]]
List[str]: List of all visions gcloud id
List[str]: List of all visions uuid
List[str]: List of all visions system_id
"""
visions: QuerySet = self.get_queryset().get_vision_conf_details(system_uuid)
gcloud: List[str] = [data[0] for data in visions]
uuid: List[str] = [data[1] for data in visions]
system_id: List[int] = [data[2] for data in visions]
return gcloud, uuid, system_id
where get_vision_conf_details is a call to my db.
How should I test my class ? Any help would be great ! thanks.