הרצת קוד אסינכרוני בבדיקות ה pytest שלכם

13/12/2019

פוסט זה כולל טיפ קצר לעבודה יעילה עם Python. אם אתם רוצים ללמוד פייתון יותר לעומק אני ממליץ על קורס Python כאן באתר.
הקורס כולל עשרות שיעורי וידאו והמון תרגול מעשי וילמד אתכם Python בצורה מקצועית מההתחלה ועד הנושאים המתקדמים.

בגלל העבודה עם קורוטינות ב asyncio שילוב קוד בדיקות איתו עלול להיות קצת מעצבן, לפחות עד שמגלים את הספריה pytest-asyncio.

הספריה pytest-asyncio היא פלאגין ל pytest שמוסיפה אפשרות לסמן בדיקות או Fixtures בתור בדיקות אסינכרוניות ובאופן אוטומטי תפעיל את הבדיקות האלה בתור קורוטינות ותעביר להן את ה event loop.

בשביל לשלב אותה בקוד הבדיקות שלכם מספיק להתקין מ pip ואז לסמן את הבדיקה בתור בדיקה אסינכרונית באופן הבא:

@pytest.mark.asyncio
async def test_luke_name(aiosession):
    data = await fetch(aiosession, 'https://swapi.co/api/people/1/')
    assert data['name'] == 'Luke Skywalker'

בדיקה זו משתמשת בפונקציית עזר בשם fetch (אסינכרונית גם היא) וב Fixture אסינכרוני בשם aiosession. הנה הקוד לשניהם:

@pytest.fixture()
async def aiosession():
    async with aiohttp.ClientSession() as session:
        yield session

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.json()

כל Fixture אסינכרוני מוגדר באופן אוטומטי להיות בעל תחום הגדרה של פונקציה. בשביל להשתמש בתחום הגדרה גדול יותר אנחנו צריכים לדרוס את ה Fixture שנקרא event_loop של ספריית pytest-asyncio ולתת לו תחום הגדרה גדול יותר. בדוגמא הבאה אני מגדיל את תחום ההגדרה של aiosession כך שיישמר לאורך כל חיי תוכנית הבדיקה:

@pytest.fixture(scope='session')
def event_loop():
    loop = asyncio.get_event_loop()
    yield loop
    loop.close()


@pytest.fixture(scope='session')
async def aiosession():
    async with aiohttp.ClientSession() as session:
        yield session

חלק גדול מהכיף בעבודה עם pytest הוא שיש פיתרונות מוכנים כמעט לכל סוג בדיקות שתרצו לכתוב. כמו בהרבה מקרים כשמדובר בפייתון שווה לעשות מאמץ ולחפש פיתרון מדף ב pip לפני שרצים לכתוב לבד.