Skip to content

Your First Test

This guide walks through a complete working example: a BDD feature file, step definitions using the Screenplay pattern, a custom Ability, and the fluent API.

1. Create a Feature File

Create features/login.feature:

Feature: User login
  As a registered user
  I want to log in to the application
  So that I can access my dashboard

  Scenario: Successful login with valid credentials
    Given Ali is a registered user
    When Ali logs in with username "admin" and password "secret123"
    Then Ali should see the dashboard

2. Define an Ability

Abilities represent what an Actor can do. Here is a simple example that wraps a mock authentication client:

# abilities.py
from screenwright import Ability


class AuthenticateWithAPI(Ability):
    """The ability to authenticate against the application API."""

    def __init__(self, client):
        self.client = client

    @staticmethod
    def using(client):
        return AuthenticateWithAPI(client)

3. Create Tasks

Tasks are high-level, business-meaningful goals. You can write them as classes or use the @task decorator.

Class-based Task

# tasks.py
from screenwright import Task


class Login(Task):
    """Log in with the given credentials."""

    def __init__(self, username: str, password: str) -> None:
        self.username = username
        self.password = password

    @staticmethod
    def with_credentials(username: str, password: str) -> "Login":
        return Login(username, password)

    def perform_as(self, actor) -> None:
        client = actor.ability_to(AuthenticateWithAPI).client
        response = client.login(self.username, self.password)
        actor.remembers(auth_token=response["token"])

Decorator-based Task

from screenwright import task


@task
def login(actor, username: str, password: str) -> None:
    """Log in with the given credentials."""
    client = actor.ability_to(AuthenticateWithAPI).client
    response = client.login(username, password)
    actor.remembers(auth_token=response["token"])

Both approaches produce the same result. The decorator style is more concise; the class style is better when you need factory methods like Login.with_credentials(...).

4. Create a Question

Questions query the current state of the system:

# questions.py
from screenwright import Question


class DashboardTitle(Question[str]):
    """What is the title shown on the dashboard?"""

    def answered_by(self, actor) -> str:
        client = actor.ability_to(AuthenticateWithAPI).client
        token = actor.recalls("auth_token")
        return client.get_dashboard(token)["title"]

Or with the decorator:

from screenwright import question


@question
def dashboard_title(actor) -> str:
    """What is the title shown on the dashboard?"""
    client = actor.ability_to(AuthenticateWithAPI).client
    token = actor.recalls("auth_token")
    return client.get_dashboard(token)["title"]

5. Write Step Definitions

Wire everything together in your step definitions using pytest-bdd:

# test_login.py
import pytest
from pytest_bdd import scenario, given, when, then, parsers

from screenwright import Actor
from abilities import AuthenticateWithAPI
from tasks import Login
from questions import DashboardTitle


@scenario("features/login.feature", "Successful login with valid credentials")
def test_successful_login():
    pass


@given("Ali is a registered user")
def ali(stage):
    mock_client = MockAPIClient()  # your test double
    return (
        stage.actor_named("Ali", description="a registered user")
        .who_can(AuthenticateWithAPI.using(mock_client))
    )


@when(
    parsers.parse('Ali logs in with username "{username}" and password "{password}"'),
)
def ali_logs_in(ali, username, password):
    ali.attempts_to(Login.with_credentials(username, password))


@then("Ali should see the dashboard")
def ali_sees_dashboard(ali):
    ali.should_see_that(DashboardTitle(), "Welcome, Ali")

6. Run It

pytest

Screenwright's pytest plugin automatically:

  1. Creates a Stage and Scene for each scenario
  2. Captures every event (actor entrance, tasks, interactions, questions)
  3. Generates a cinematic HTML report in screenwright-report/

Open screenwright-report/index.html in your browser to see the result.

What Just Happened?

Here is what Screenwright did behind the scenes:

  1. FeatureStarted -- recorded that the "User login" feature began
  2. SceneStarted -- recorded the "Successful login" scenario
  3. ActorEnteredStage -- Ali was placed on the Stage
  4. AbilityGranted -- Ali received the AuthenticateWithAPI ability
  5. TaskStarted / TaskCompleted -- the Login task was performed
  6. FactRemembered -- Ali remembered the auth_token
  7. QuestionAsked / QuestionAnswered -- DashboardTitle was checked
  8. SceneEnded -- the scenario completed with a "passed" outcome
  9. FeatureEnded -- the feature file finished

All of these events are stored in screenwright-report/event-stream.json and rendered into the HTML report.