Fragments

Fragments are reusable scenario steps that can be included in multiple scenarios. They help reduce duplication and improve maintainability of your test suites.

Creating Fragments

Fragment files use the .fragment extension and are placed in the src/test/resources/berrycrush/fragments/ directory by default.

berrycrush/fragments/auth.fragment:

# Authentication Fragment
# Reusable authentication steps for protected endpoints

fragment: authenticate
  given I have valid credentials
    call using auth ^login
      body: {"username": "test", "password": "test"}
    extract $.token => authToken
  then authentication is successful
    assert status 200

fragment: logout
  when I log out
    call using auth ^logout
  then the session is terminated
    assert status 200

Fragment Structure

Each fragment starts with fragment: <name> followed by indented steps:

fragment: <fragment-name>
  <step-type> <description>
    <actions>
  <step-type> <description>
    <actions>

Using Fragments

Include fragments in your scenarios using the include directive:

scenarios/list-pets.scenario:

scenario: Authenticated list pets
  given I authenticate first
    include authenticate
  when I request the list of pets
    call ^listPets
  then I get a successful response
    assert status 200

The steps from the authenticate fragment are expanded inline during execution.

Fragment Discovery

BerryCrush discovers fragment files using patterns specified in the @BerryCrushScenarios annotation.

Default Discovery

By default, BerryCrush searches for fragment files matching the pattern berrycrush/fragments/*.fragment:

@BerryCrushScenarios(locations = ["berrycrush/scenarios/*.scenario"])
// fragments = ["berrycrush/fragments/*.fragment"] is the default
class MyApiTest

Custom Fragment Locations

You can specify custom fragment locations using the fragments property:

@BerryCrushScenarios(
    locations = ["scenarios/*.scenario"],
    fragments = ["shared/fragments/*.fragment", "auth/*.fragment"]
)
class MyApiTest

The fragments property accepts an array of glob patterns, allowing you to:

  • Search multiple directories

  • Use wildcard patterns (*, **)

  • Specify exact file paths

Examples:

// Single directory
fragments = ["my-fragments/*.fragment"]

// Multiple directories
fragments = ["auth/*.fragment", "common/*.fragment", "api/*.fragment"]

// Recursive search
fragments = ["**/*.fragment"]

// Specific files
fragments = ["fragments/auth.fragment", "fragments/setup.fragment"]

Directory Structure

Typical project structure with custom fragment locations:

src/test/resources/
├── petstore.yaml
├── auth/
│   └── auth.fragment
├── common/
│   └── setup.fragment
└── scenarios/
    ├── pet-crud.scenario
    └── user-api.scenario

With annotation:

@BerryCrushScenarios(
    locations = ["scenarios/*.scenario"],
    fragments = ["auth/*.fragment", "common/*.fragment"]
)
class MyApiTest

Variables in Fragments

Fragments can extract values that become available in the calling scenario:

fragment: authenticate
  given I have valid credentials
    call using auth ^login
      body: {"username": "test", "password": "test"}
    extract $.token => authToken

The authToken variable is now available in the scenario that included this fragment:

scenario: Access protected resource
  given I am authenticated
    include authenticate
  when I access the protected endpoint
    call ^getSecretData
      header_Authorization: Bearer {{authToken}}
  then I get the data
    assert status 200

Parameterized Fragments

Fragments can accept parameters to make them more flexible and reusable. Parameters are passed when including the fragment and become available as variables within the fragment’s scope.

Defining Parameterized Fragments

A fragment can use variables that will be provided by the calling scenario:

fragment: create_user
  when creating the user
    call ^createUser
      body: {"name": "{{name}}", "email": "{{email}}", "age": {{age}}}
    extract $.id => userId
  then user is created
    assert status 201

Passing Parameters

Use indented key-value pairs after the include directive to pass parameters:

scenario: Create a specific user
  given I create a user
    include create_user
      name: "John Doe"
      email: "john@example.com"
      age: 30
  then the user exists
    assert $.name equals "John Doe"

Parameter Types

Parameters support various value types:

include configure_pet
  name: "Fluffy"           # String (must be quoted if spaces/special chars)
  petId: 123               # Number
  active: true             # Boolean
  tags: ["cute", "small"]  # JSON array
  metadata: {"key": "val"} # JSON object

Using Variable References

Parameters can reference existing variables using {{variableName}} syntax:

scenario: Create user from context
  given I have user data
    set userName => "Alice"
    set userEmail => "alice@example.com"
  when I create the user
    include create_user
      name: {{userName}}
      email: {{userEmail}}
      age: 25
  then the user exists in the system

Complete Example

fragments/crud.fragment:

fragment: create_entity
  when creating the entity
    call ^createEntity
      body: {"type": "{{entityType}}", "name": "{{name}}", "owner": "{{owner}}"}
    extract $.id => entityId
  then entity is created
    assert status 201

fragment: delete_entity
  when deleting the entity
    call ^deleteEntity
      id: {{entityId}}
  then entity is removed
    assert status 204

scenarios/entity-test.scenario:

scenario: Full entity lifecycle
  given I create a pet entity
    include create_entity
      entityType: "pet"
      name: "Buddy"
      owner: "john@example.com"
  then I clean up
    include delete_entity
      entityId: {{entityId}}

Multi-Spec in Fragments

Fragments can use the using keyword to call operations from named specs:

fragment: admin-login
  given I have admin credentials
    call using auth ^login
      body: {"username": "admin", "password": "admin123"}
    extract $.token => adminToken
  then admin authentication is successful
    assert status 200

Example: Complete Test Suite

Directory structure:

src/test/resources/
├── petstore.yaml
├── auth.yaml
├── fragments/
│   └── auth.fragment
└── scenarios/
    ├── 01-setup.scenario
    ├── pet-crud.scenario
    └── 99-cleanup.scenario

fragments/auth.fragment:

fragment: authenticate
  given I have valid credentials
    call using auth ^login
      body: {"username": "test", "password": "test"}
    extract $.token => authToken
  then authentication is successful
    assert status 200

fragment: authenticate-admin
  given I have admin credentials
    call using auth ^login
      body: {"username": "admin", "password": "admin123"}
    extract $.token => adminToken
  then admin authentication is successful
    assert status 200

scenarios/pet-crud.scenario:

scenario: Create pet as authenticated user
  given I am authenticated
    include authenticate
  when I create a new pet
    call ^createPet
      header_Authorization: Bearer {{authToken}}
      body: {"name": "Fluffy", "status": "available"}
  then the pet is created
    assert status 201
    extract $.id => petId

scenario: Delete pet as admin
  given I am an admin
    include authenticate-admin
  when I delete the pet
    call ^deletePet
      header_Authorization: Bearer {{adminToken}}
      petId: {{petId}}
  then the pet is deleted
    assert status 204

Best Practices

  1. Keep fragments focused: Each fragment should do one thing well

  2. Use descriptive names: Fragment names should clearly indicate their purpose

  3. Document variables: Comment which variables are extracted by the fragment

  4. Organize by domain: Group related fragments together

  5. Avoid deep nesting: Don’t include fragments within fragments