Skip to content

Dynamic Features

Bryan edited this page Jan 30, 2026 · 7 revisions

SpecForge offers several ways to make your tests dynamic and data-driven through expressions, data generation, and variable management.

Expressions Overview

Expressions are dynamic values evaluated at runtime using the {{ }} syntax:

- name: "Create user"
  request:
    url: /users
    http_verb: POST
    json:
      email: "{{ faker.internet.email }}"
      role: "{{ default_role }}"
  store:
    user_id: "{{ response.body.id }}"

See Step Reference for the complete expression reference.

Data Generation

Using Faker

Access any Faker method using the faker. prefix:

- name: "Create user with fake data"
  request:
    url: /users
    http_verb: POST
    json:
      name: "{{ faker.name.name }}"
      email: "{{ faker.internet.email }}"
      phone: "{{ faker.phone_number.phone_number }}"
      address:
        street: "{{ faker.address.street_address }}"
        city: "{{ faker.address.city }}"
        state: "{{ faker.address.state }}"
        zip: "{{ faker.address.zip_code }}"

Faker with Arguments

When a Faker method needs arguments, use key position (no {{ }}):

- store:
    age:
      faker.number.between:
        from: 18
        to: 65
    price:
      faker.number.decimal:
        l_digits: 2
        r_digits: 2

- name: "Use generated values"
  request:
    url: /products
    http_verb: POST
    json:
      price: "{{ price }}"
      min_age: "{{ age }}"

Nested Faker Classes

Faker's nested namespaces work naturally:

- name: "Game-themed test data"
  request:
    url: /characters
    http_verb: POST
    json:
      zelda_character: "{{ faker.games.zelda.character }}"
      pokemon: "{{ faker.games.pokemon.name }}"
      superhero: "{{ faker.superhero.name }}"

Using Factories

Generate test objects using FactoryBot through the factories. prefix:

- store:
    user: "{{ factories.user }}"
    admin: "{{ factories.user.admin }}"  # Using a trait

- name: "Create post for user"
  request:
    url: /posts
    http_verb: POST
    json:
      title: "{{ faker.lorem.sentence }}"
      user_id: "{{ user.id }}"

Factory Attributes

Access any attribute on a factory-created object:

- store:
    author: "{{ factories.user }}"

- name: "Verify author data"
  request:
    url: "/users/{{ author.id }}"
  expect:
  - status: 200
    json:
      content:
        name: "{{ author.name }}"
        email: "{{ author.email }}"

See Factory Support for factory configuration and advanced usage.

Generating Arrays

Use generate.array to create arrays of dynamic values:

- name: "Bulk create users"
  request:
    url: /users/bulk
    http_verb: POST
    json:
      users:
        generate.array:
          size: 10
          value:
            name: "{{ faker.name.name }}"
            email: "{{ faker.internet.email }}"

Using the Index Variable

Inside generate.array, the special index variable (0-based) is available:

- name: "Create numbered items"
  request:
    url: /items/bulk
    http_verb: POST
    json:
      items:
        generate.array:
          size: 5
          value:
            position: "{{ index }}"
            name: "Item {{ index }}"

This produces: [{position: 0, name: "Item 0"}, {position: 1, name: "Item 1"}, ...]

Method Chaining

Chain methods on any resolved value:

- store:
    # Chain on faker results
    lowercase_email: "{{ faker.internet.email.downcase }}"
    uppercased_name: "{{ faker.name.name.upcase }}"
    
    # Chain on factory attributes
    user: "{{ factories.user }}"
    first_post_title: "{{ user.posts.first.title }}"
    
    # Array access with indices (zero-based)
    second_comment: "{{ user.posts.0.comments.1.body }}"

Chaining works on:

  • Faker results
  • Factory objects and their associations
  • Variables (including nested hashes and arrays)
  • Response data

Environment Variables

Access environment variables with the env. prefix:

- name: "Authenticated request"
  request:
    url: /admin/users
    headers:
      Authorization: "Bearer {{ env.API_TOKEN }}"
      X-Api-Key: "{{ env.API_KEY }}"

This is useful for:

  • API tokens and secrets (keep out of version control)
  • Environment-specific configuration
  • CI/CD pipeline variables

Variables

Local Variables with store:

Define variables for use in subsequent steps:

- store:
    base_email: "test@example.com"
    default_role: "user"

- name: "Create user"
  request:
    url: /users
    http_verb: POST
    json:
      email: "{{ base_email }}"
      role: "{{ default_role }}"

Capturing Response Data

When a step has request:, capture response values:

- name: "Create user"
  request:
    url: /users
    http_verb: POST
    json:
      name: "{{ faker.name.name }}"
  store:
    user_id: "{{ response.body.id }}"
    user_email: "{{ response.body.email }}"

- name: "Get created user"
  request:
    url: "/users/{{ user_id }}"
  expect:
  - status: 200
    json:
      content:
        email: "{{ user_email }}"

Global Variables

Define variables in forge_helper.rb for use across all blueprints:

SpecForge.configure do |config|
  config.global_variables = {
    api_version: "v1",
    default_role: "user",
    admin_email: "admin@test.com"
  }
end
- name: "Versioned API request"
  request:
    url: "/api/{{ api_version }}/users"
    json:
      role: "{{ default_role }}"

See Context System for detailed coverage of variable scoping, shadowing, and the store: system.

Related Documentation

Clone this wiki locally