Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions docs/best-practices.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ session.add_suite(integration_suite)
# DON'T DO THIS - 500 lines of mixed fixtures/suites/tests
session = ProTestSession()

@fixture()
@fixture
def db(): ...

@fixture()
@fixture
def cache(): ...

session.bind(db)
Expand Down Expand Up @@ -164,7 +164,7 @@ di_suite = ProTestSuite(
|-------|-----------------|----------|
| Session | `session.py` or `fixtures.py` | Database, expensive resources |
| Suite | Suite file | Suite-specific setup |
| Test | Near test, `@fixture()` | Test isolation, cheap resources |
| Test | Near test, `@fixture` | Test isolation, cheap resources |

### Session Fixtures: Expensive Resources

Expand All @@ -189,7 +189,7 @@ from protest import ProTestSuite, fixture

api_suite = ProTestSuite("API")

@fixture()
@fixture
async def authenticated_client(
db: Annotated[Database, Use(database)]
) -> AsyncGenerator[APIClient, None]:
Expand All @@ -204,8 +204,8 @@ api_suite.bind(authenticated_client)
### Test Fixtures: Fresh Per Test

```python
# Use @fixture() (not scoped) for per-test isolation
@fixture()
# Use @fixture (not scoped) for per-test isolation
@fixture
def request_payload() -> dict:
return {"name": "test", "value": 42}
```
Expand Down Expand Up @@ -284,13 +284,13 @@ ProTestSuite("Misc") # Bad

```python
# Name after what they provide
@fixture()
@fixture
def database(): ... # Provides a database

@factory()
def user(): ... # Provides a user (becomes user_factory in tests)

@fixture()
@fixture
def api_client(): ... # Provides an API client
```

Expand Down Expand Up @@ -435,12 +435,12 @@ def convert_video(input_path: str, output_path: str) -> ConversionResult:

```python
# BAD: Should be test-scoped but bound to session
@fixture()
@fixture
def request_id(): ...
session.bind(request_id) # Fresh value needed per test!

# GOOD: Test-scoped (not bound)
@fixture()
@fixture
def request_id() -> str:
return str(uuid4())
```
Expand Down Expand Up @@ -508,4 +508,4 @@ async def test_draft_cannot_be_archived_directly(
- [Fixtures](core-concepts/fixtures.md)
- [Factories](core-concepts/factories.md)
- [Tags](core-concepts/tags.md) - Tag inheritance and filtering
- [CLI Reference](cli.md)
- [CLI Reference](cli.md)
10 changes: 5 additions & 5 deletions docs/core-concepts/dependency-injection.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ from protest import ProTestSession, Use, fixture

session = ProTestSession()

@fixture()
@fixture
def database():
return Database()

Expand Down Expand Up @@ -65,11 +65,11 @@ def undefined_fixture():
Fixtures can depend on other fixtures:

```python
@fixture()
@fixture
def config():
return {"db_url": "postgres://localhost"}

@fixture()
@fixture
async def database(cfg: Annotated[dict, Use(config)]):
return await connect(cfg["db_url"])

Expand Down Expand Up @@ -109,11 +109,11 @@ If two tests both use `database`, and `database` is session-scoped, they share t
Raised when a fixture depends on a narrower scope:

```python
@fixture() # Test scope (not bound)
@fixture # Test scope (not bound)
def per_test():
return "fresh"

@fixture()
@fixture
def shared(x: Annotated[str, Use(per_test)]):
return x

Expand Down
2 changes: 1 addition & 1 deletion docs/core-concepts/factories.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ If a test creates `alice` then `bob`, teardown runs `bob` first, then `alice`.
Factories can depend on other fixtures:

```python
@fixture()
@fixture
def database():
return Database()

Expand Down
28 changes: 14 additions & 14 deletions docs/core-concepts/fixtures.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ Fixtures provide reusable setup and teardown logic for tests.

## What is a Fixture?

A fixture is a function decorated with `@fixture()` that provides a value to tests.
A fixture is a function decorated with `@fixture` that provides a value to tests.

```python
from protest import fixture

@fixture()
@fixture
def config():
return {"debug": True}
```
Expand All @@ -18,7 +18,7 @@ def config():

**Important:** Fixture scope is determined by WHERE you bind it, not by the decorator.

The `@fixture()` decorator only marks a function as a fixture. The scope is set when you bind the fixture to a session or suite.
The `@fixture` decorator only marks a function as a fixture. The scope is set when you bind the fixture to a session or suite.

### Session Scope

Expand All @@ -27,7 +27,7 @@ Bind to session with `session.bind()`. Lives for the entire test session.
```python
from protest import fixture, ProTestSession

@fixture()
@fixture
async def database():
db = await connect()
yield db
Expand All @@ -46,7 +46,7 @@ Bind to suite with `suite.bind()`. Lives for the duration of the suite.
```python
from protest import fixture, ProTestSuite

@fixture()
@fixture
def api_client(db: Annotated[Database, Use(database)]):
return Client(db)

Expand All @@ -61,7 +61,7 @@ Use case: Resources shared within a group of related tests.
Don't bind the fixture. Fresh instance for each test.

```python
@fixture()
@fixture
def request_id():
return str(uuid.uuid4())

Expand All @@ -83,7 +83,7 @@ Use case: Isolated state per test, unique IDs, temporary files.
Use `yield` to separate setup from teardown:

```python
@fixture()
@fixture
async def database():
# Setup
db = await connect()
Expand Down Expand Up @@ -111,11 +111,11 @@ A fixture can only depend on fixtures with equal or wider scope:
Violating this raises `ScopeMismatchError`:

```python
@fixture()
@fixture
def per_test_data():
return "per-test"

@fixture()
@fixture
def shared_resource(x: Annotated[str, Use(per_test_data)]):
return x

Expand Down Expand Up @@ -210,7 +210,7 @@ async def rate_limited_api():
"""Only 2 concurrent requests allowed."""
yield ApiClient()

@fixture()
@fixture
async def user_service(api: Annotated[ApiClient, Use(rate_limited_api)]):
"""Service that uses the rate-limited API."""
yield UserService(api)
Expand Down Expand Up @@ -242,7 +242,7 @@ Autouse fixtures are automatically resolved at their scope start, without being
### Session Autouse

```python
@fixture()
@fixture
def configure_logging():
logging.basicConfig(level=logging.DEBUG)
yield
Expand All @@ -256,7 +256,7 @@ Session autouse fixtures are resolved at `SESSION_SETUP_START`, before any test
### Suite Autouse

```python
@fixture()
@fixture
def clear_environment():
old = os.environ.copy()
os.environ.clear()
Expand All @@ -280,10 +280,10 @@ Don't use autouse when tests need the fixture's value - use explicit `Use()` ins

## Plain Functions

All fixtures must be decorated with `@fixture()`. Plain functions raise `PlainFunctionError` when used with `Use()`:
All fixtures must be decorated with `@fixture`. Plain functions raise `PlainFunctionError` when used with `Use()`:

```python
def not_a_fixture(): # Missing @fixture()!
def not_a_fixture(): # Missing @fixture!
return "value"

@session.test()
Expand Down
6 changes: 3 additions & 3 deletions docs/core-concepts/sessions-and-suites.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Bind fixtures to the session with `bind()`:
```python
from protest import fixture

@fixture()
@fixture
def database():
db = connect()
yield db
Expand Down Expand Up @@ -79,7 +79,7 @@ api_suite = ProTestSuite(
Bind fixtures to a suite with `bind()`:

```python
@fixture()
@fixture
def api_client():
return Client()

Expand Down Expand Up @@ -124,7 +124,7 @@ orders_suite.full_path # "API::Orders"
Child suites can access fixtures from parent suites:

```python
@fixture()
@fixture
def api_client():
return Client()

Expand Down
4 changes: 2 additions & 2 deletions docs/core-concepts/tags.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,15 @@ session.bind(db)


# Fixture that depends on db - inherits "database" tag
@fixture()
@fixture
def user_repository(db: Annotated[Database, Use(db)]):
return UserRepository(db)

session.bind(user_repository)


# Fixture that depends on user_repository - also inherits "database"
@fixture()
@fixture
def user_service(repo: Annotated[UserRepository, Use(user_repository)]):
return UserService(repo)

Expand Down
4 changes: 2 additions & 2 deletions docs/core-concepts/tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Tests declare their dependencies using type annotations:
from typing import Annotated
from protest import Use, fixture

@fixture()
@fixture
def database():
return Database()

Expand Down Expand Up @@ -175,7 +175,7 @@ ProTest evaluates skip conditions **after** fixture resolution, so your callable
from typing import Annotated
from protest import Use, fixture

@fixture()
@fixture
def environment():
return {"is_ci": os.getenv("CI") == "true"}

Expand Down
9 changes: 4 additions & 5 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ from typing import Annotated
user_suite = ProTestSuite("User")


@fixture()
@fixture
def user():
return User("alice")

Expand Down Expand Up @@ -95,12 +95,12 @@ If fixtures could use `From()`, you'd get hidden cartesian products:
```python
# Hypothetical - NOT SUPPORTED (decorator syntax doesn't exist either)
# If this were possible:
@fixture()
@fixture
def db(engine: Annotated[str, From(ENGINES)]): # 3 engines
...
session.bind(db)

@fixture()
@fixture
def user(role: Annotated[str, From(ROLES)], db: ...): # 2 roles
...
session.bind(user)
Expand Down Expand Up @@ -164,5 +164,4 @@ async def test_pipeline() -> None:
assert result.success
```

See `examples/subprocess_capture/session.py` for complete examples and [Built-ins](core-concepts/builtins.md#shell) for full API.

See `examples/subprocess_capture/session.py` for complete examples and [Built-ins](core-concepts/builtins.md#shell) for full API.
2 changes: 1 addition & 1 deletion docs/getting-started/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ session.add_suite(api_suite)
# Define a test-scoped fixture (fresh instance per test)
# For session scope: session.bind(fixture_fn)
# For suite scope: suite.bind(fixture_fn)
@fixture()
@fixture
def config():
return {"api_url": "https://api.example.com"}

Expand Down
Loading
Loading