Spex

Hex.pmDocumentation

Behavior-Driven Development (BDD) for AI-Driven Testing | Executable Specifications | Specification by Example

SexySpex is a behavior-driven development (BDD) framework for Elixir that enables executable specifications, specification by example, and AI-driven testing. Write Given-When-Then scenarios that serve as both living documentation and automated tests.

Features

Installation

Add sexy_spex to your list of dependencies in mix.exs:

def deps do
  [
    {:sexy_spex, "~> 0.1.0"}
  ]
end

Quick Start

1. Write Your First Spex

Create a file test/spex/user_registration_spex.exs:

defmodule MyApp.UserRegistrationSpex do
  use SexySpex

  setup_all do
    # Start your application or setup shared state
    {:ok, %{base_url: "http://localhost:4000"}}
  end

  spex "user can register successfully",
    description: "Validates the user registration flow",
    tags: [:user_management, :registration] do
    
    scenario "with valid data", context do
      given_ "valid user registration data", context do
        user_data = %{
          email: "test@example.com",
          password: "secure_password123",
          name: "Test User"
        }
        assert valid_registration_data?(user_data)
        Map.put(context, :user_data, user_data)
      end

      when_ "user submits registration", context do
        {:ok, user} = MyApp.Users.register(context.user_data)
        assert user.email == context.user_data.email
        Map.put(context, :user, user)
      end

      then_ "user account is created and can login", context do
        assert {:ok, _session} = MyApp.Auth.login(
          context.user_data.email, 
          context.user_data.password
        )
      end
    end
  end
end

2. Run Your Spex

# Run all spex files
mix spex

# Run specific spex file
mix spex test/spex/user_registration_spex.exs

# Run with verbose output
mix spex --verbose

# Run in manual mode (step-by-step)
mix spex --manual

Important: Spex files can ONLY be run with mix spex, not mix test. This ensures proper compilation and application lifecycle management.

3. See Beautiful Output

🎯 Running Spex: user can register successfully
==================================================
   Validates the user registration flow
   Tags: #user_management #registration

  📋 Scenario: with valid data
    Given: valid user registration data
    When: user submits registration
    Then: user account is created and can login
  ✅ Scenario passed: with valid data

✅ Spex completed: user can register successfully

GUI Testing with Scenic

For Scenic applications, use the built-in helpers:

defmodule MyGUI.LoginSpex do
  use SexySpex

  setup_all do
    # Start Scenic application with MCP server
    SexySpex.Helpers.start_scenic_app(:my_gui_app)
  end

  spex "user can login via GUI", context do
    scenario "successful login flow", context do
      given_ "the application is running", context do
        assert SexySpex.Helpers.application_running?(:my_gui_app)
        assert SexySpex.Helpers.can_connect_to_scenic_mcp?(context.port)
      end

      when_ "user enters valid credentials", context do
        # Use scenic_mcp tools for interaction
        ScenicMcp.send_keys(text: "user@example.com")
        ScenicMcp.send_keys(key: "tab")
        ScenicMcp.send_keys(text: "password123")
        ScenicMcp.send_keys(key: "enter")
      end

      then_ "user is logged in successfully", context do
        # Take screenshot for verification
        ScenicMcp.take_screenshot(filename: "logged_in_dashboard")
        viewport_state = ScenicMcp.Probes.viewport_state()
        assert viewport_state.name == :main_viewport
      end
    end
  end
end

Framework Helpers

SexySpex provides semantic helpers for common patterns:

# Start Scenic applications with MCP server
SexySpex.Helpers.start_scenic_app(:quillex)
SexySpex.Helpers.start_scenic_app(:flamelex, port: 8888)

# Test connectivity
SexySpex.Helpers.can_connect_to_scenic_mcp?(9999)
SexySpex.Helpers.application_running?(:my_app)

# Automatically handles:
# - Compilation (Mix.Task.run("compile"))
# - Application startup and cleanup
# - MCP server waiting and connection testing

Manual Mode - Interactive Testing

Run spex in manual mode for step-by-step execution:

mix spex --manual

Manual mode gives you:

Perfect for:

Architecture

Built on ExUnit

SexySpex is 100% built on ExUnit but provides a controlled execution environment:

# When you write:
use SexySpex

# You get:
use ExUnit.Case, async: false  # Standard ExUnit test case
import SexySpex.DSL               # spex/scenario/given_/when_/then_

Execution Flow

mix spex → Mix.Tasks.Spex → ExUnit.start() → Load spex files → ExUnit.run()

Core Modules

Use Cases

Perfect for teams practicing:

Philosophy

Spex bridges the gap between business requirements and automated testing by providing:

This enables AI systems to write, execute, and understand tests while maintaining human readability and stakeholder collaboration.

📚 Documentation

Contributing

We welcome contributions! Please see the documentation in /docs for details.

License

This project is licensed under the MIT License.

Inspiration

Spex is inspired by:

Perfect for teams building the future of AI-assisted software development.