Lifecycle & hooks
Introduction
CSTesting for Python (C:\CSTesting-Python) uses functions, not Java annotations. Inside a describe suite you register hooks and tests with before_all, after_all, beforeEach / before_each, afterEach / after_each, and it. Use describe.only / it.only and describe.skip / it.skip like Jest. The implementation is in cstesting/runner.py.
Hooks and tests (Python)
| API | When it runs |
|---|---|
before_all(fn) | Once at the start of this suite (after the parent suite has started its own work). |
after_all(fn) | Once after this suite’s tests and nested child suites finish. |
beforeEach(fn) / before_each(fn) | Before each it in this suite. The runner does not pass a browser argument—open Playwright yourself (see below). |
afterEach(fn) / after_each(fn) | After each it in this suite (still runs if the test failed). |
it("name", fn) | One test. fn may be sync or async def; it takes no parameters from the runner. |
Register hooks by calling before_all(my_fn) inside the suite function. Hooks can be sync or async; the runner awaits coroutines. Multiple beforeEach registrations run in order.
Execution order
Implementation in cstesting/runner.py for each describe suite:
before_all- Every
itregistered directly on this suite: for each, runbeforeEachhooks, the test, thenafterEachhooks - Each nested
describechild suite (full subtree) after_all
Define a suite with describe("Name", _suite) where _suite is a function that calls before_all, it, nested describe, etc. This matches README.md and example/math_test.py in C:\CSTesting-Python.
Browser with hooks
The runner does not create a Playwright browser for you. Typical pattern: create_browser in before_all, store on a module-level variable (or page object), browser.close() in after_all—same as README.md and templates/tests/home_test.py. For a fresh browser per test, call create_browser inside each it (with try/finally).
Headed mode: use create_browser(headless=False) (CLI --headed applies to config-driven runs, not to this hook pattern).
it tests
import asyncio
from cstesting import describe, it, expect, before_all, after_all, create_browser
browser = None
def _suite():
def _before():
global browser
loop = asyncio.get_event_loop()
browser = 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 _login():
await browser.goto("https://example.com/login")
await browser.type("name=user", "alice")
await browser.click('button[type="submit"]')
await browser.wait_for_url("**/welcome", {"timeout": 10000})
it("redirects after login", _login)
async def _title():
await browser.goto("https://example.com")
title = await browser.evaluate("document.title")
expect(title).to_equal("Example")
it("shows title", _title)
describe("Login", _suite)
Use it.only / describe.only to focus one test or suite; it.skip / describe.skip to skip.
Runner: CLI and run()
python -m cstesting
python -m cstesting example/math_test.py
python -m cstesting tests/
python -m cstesting --tag smoke "**/*.test.py"
Programmatically (after registering suites at import time): from cstesting import run then result = run(). See README.md in C:\CSTesting-Python.
Full example (hooks only)
from cstesting import describe, it, before_all, after_all, beforeEach, afterEach, expect
def _suite():
def suite_setup():
print("before all")
def suite_teardown():
print("after all")
def before_each_test():
print("before each")
def after_each_test():
print("after each")
before_all(suite_setup)
after_all(suite_teardown)
beforeEach(before_each_test)
afterEach(after_each_test)
it("one", lambda: expect(1).to_be(1))
it("two", lambda: expect(2).to_be(2))
describe("Example suite", _suite)
Note: Register hooks with before_all(fn) etc. Do not use @before_all as a decorator—those helpers return None and would replace your function name.
Run: python -m cstesting your_file.py
Nested describe
Inside _suite, call describe("Child", _child_suite) to add a nested suite. Child suites run after all it calls registered on the parent in the same _suite function (see runner.py).
Summary
| Topic | Python CSTesting |
|---|---|
| Test case | it("name", fn) — sync or async, no runner-injected args |
| Suite | describe("name", suite_fn) |
| Once per suite | before_all(fn), after_all(fn) |
| Per test | beforeEach(fn), afterEach(fn) (aliases before_each, after_each) |
| Browser | You call create_browser / close; not injected by the runner |
| Assertions | expect(...) |
| CLI | python -m cstesting [path|glob], tags via --tag |