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).
| Matcher | Example |
|---|---|
to_be | expect(1 + 1).to_be(2) |
to_equal | expect({"a": 1}).to_equal({"a": 1}) |
to_be_truthy / to_be_falsy | expect(x).to_be_truthy() |
to_contain | expect("hello").to_contain("ell") |
to_have_length | expect([1, 2, 3]).to_have_length(3) |
to_throw | expect(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()