Writing tests

Introduction

CSTesting for Python (C:\CSTesting-Python) uses describe / it from cstesting and expect(actual) for assertions (Jest-style). Browser automation is async (Playwright): use async def tests and await on browser.goto, locator.click(), etc. Install with browser extras: pip install -e ".[browser]" and playwright install chromium.

The example below matches the patterns in the project example/ and README.md.

import asyncio
from cstesting import describe, it, expect, before_all, after_all, create_browser

browser = None

def _suite():
    def _before():
        global browser
        browser = asyncio.get_event_loop().run_until_complete(
            create_browser(headless=True)
        )

    def _after():
        global browser
        if browser:
            asyncio.get_event_loop().run_until_complete(browser.close())

    before_all(_before)
    after_all(_after)

    async def _test():
        await browser.goto("https://example.com")
        title = await browser.evaluate("document.title")
        expect(title).to_contain("Example")
        h1 = await browser.locator("h1").first().text_content()
        expect(h1).to_contain("Example")

    it("loads example.com", _test)

describe("Browser smoke", _suite)

Assertions

Use from cstesting import expect. Matchers throw AssertionError on failure. Negate with expect(x).not_.to_be(y) (Python reserves not).

MatcherExample
to_beexpect(1 + 1).to_be(2)
to_equalexpect({"a": 1}).to_equal({"a": 1})
to_be_truthy / to_be_falsyexpect(x).to_be_truthy()
to_containexpect("hello").to_contain("ell")
to_have_lengthexpect([1, 2, 3]).to_have_length(3)
to_throwexpect(lambda: 1/0).to_throw()

Page title / URL: there is no assertThat().hasTitle in Python—read values then assert:

title = await browser.evaluate("document.title")
expect(title).to_contain("Example Domain")

current = await browser.url()
expect("example.com" in current).to_be_truthy()

Locators

Create locators with browser.locator(selector). Actions are async: await browser.locator("button").click(), await browser.click("#id"). For multiple matches use .first(), .last(), or .nth(n).

Selector shortcuts (same as Node): id=, name=, class=, CSS, or XPath starting with //.

await browser.locator("name=email").type("user@example.com")
await browser.locator("//button[contains(.,'Sign in')]").first().click()
text = await browser.locator("h1").first().text_content()
expect(text).to_contain("Welcome")

See Locators for selector formats and Assertions for expect.

Test isolation

Use before_all / after_all for one browser per suite (as in templates/tests/home_test.py), or launch a new browser inside each it if you need a fresh context per test. Always await browser.close() in after_all (or finally).

async def _login_test():
    b = await create_browser(headless=True)
    try:
        await b.goto("https://example.com/login")
        await b.locator("name=user").type("alice")
        await b.locator("name=pass").type("secret")
        await b.locator('button[type="submit"]').click()
        await b.wait_for_url("**/welcome", {"timeout": 10000})
    finally:
        await b.close()