Documentation
¶
Overview ¶
Package cucumber provides a godog-based BDD test framework with HTTP API testing support.
Variables are scoped to the scenario. HTTP response state is stored in the user's session. Switching users switches the session. Scenarios are executed concurrently.
Variable resolution supports:
- ${variableName} → scenario variable lookup
- ${response.body} → full HTTP response body (Java feature file compat)
- ${response.body.field} → response body field via gojq (Java feature file compat)
- ${response.field} → response body field via gojq
- ${variable.field} → nested field access
- ${variable | pipe} → pipe transformations (json, json_escape, string)
Index ¶
- Variables
- func ApplyReportOptions(opts *godog.Options, testName string) func()
- func DefaultOptions() godog.Options
- func ToString(value interface{}, name string, encoding Encoding) (string, error)
- type Encoding
- type ScenarioSetupFunc
- type TaskRow
- type TestDB
- type TestScenario
- func (s *TestScenario) APIBaseURL() string
- func (s *TestScenario) EncodingMustMatch(encoding Encoding, actual, expected string, expandExpected bool) error
- func (s *TestScenario) Expand(value string, skippedVars ...string) (result string, rerr error)
- func (s *TestScenario) FilterQueryRows(rows []map[string]interface{}) []map[string]interface{}
- func (s *TestScenario) IsolatedClientID(clientID string) string
- func (s *TestScenario) IsolatedUser(name string) string
- func (s *TestScenario) JSONMustContain(actual, expected string, expand bool) error
- func (s *TestScenario) JSONMustMatch(actual, expected string, expandExpected bool) error
- func (s *TestScenario) NormalizeResponseBody(requestURL, contentType string, body []byte) ([]byte, error)
- func (s *TestScenario) NormalizeUsers(text string) string
- func (s *TestScenario) NormalizeValue(value interface{}) interface{}
- func (s *TestScenario) RegisterCanonicalUsers(names ...string)
- func (s *TestScenario) Resolve(name string) (interface{}, error)
- func (s *TestScenario) ResolveString(name string) (string, error)
- func (s *TestScenario) RewriteQuotedUsers(text string) string
- func (s *TestScenario) RewriteRequestBody(body string) string
- func (s *TestScenario) RewriteRequestPath(path string) string
- func (s *TestScenario) SelectChild(value any, path string) (any, error)
- func (s *TestScenario) SendHTTPRequestWithJSONBody(method, path string, jsonTxt *godog.DocString) error
- func (s *TestScenario) SendHTTPRequestWithJSONBodyAndStyle(method, path string, jsonTxt *godog.DocString, eventStream bool, ...) (err error)
- func (s *TestScenario) SendHTTPRequestWithJSONBodyAsEventStream(method, path string, jsonTxt *godog.DocString) error
- func (s *TestScenario) Session() *TestSession
- func (s *TestScenario) TestDB() TestDB
- func (s *TestScenario) TheResponseShouldContainJSONDoc(expected *godog.DocString) error
- func (s *TestScenario) TheResponseShouldMatchJSONDoc(expected *godog.DocString) error
- func (s *TestScenario) User() *TestUser
- type TestSession
- type TestSuite
- type TestUser
Constants ¶
This section is empty.
Variables ¶
var JSONEncoding = Encoding{ Name: "json", Marshal: func(a any) ([]byte, error) { return json.MarshalIndent(a, "", " ") }, Unmarshal: json.Unmarshal, }
var PipeFunctions = map[string]func(any, error) (any, error){ "json": func(value any, err error) (any, error) { if err != nil { return value, err } buf := bytes.NewBuffer(nil) encoder := json.NewEncoder(buf) encoder.SetIndent("", " ") err = encoder.Encode(value) if err != nil { return value, err } return buf.String(), err }, "json_escape": func(value any, err error) (any, error) { if err != nil { return value, err } data, err := json.Marshal(fmt.Sprintf("%v", value)) if err != nil { return value, err } return strings.TrimSuffix(strings.TrimPrefix(string(data), `"`), `"`), nil }, "string": func(value any, err error) (any, error) { if err != nil { return value, err } return fmt.Sprintf("%v", value), nil }, "uuid_to_hex_string": func(value any, err error) (any, error) { if err != nil { return value, err } s := fmt.Sprintf("%v", value) id, err := uuid.Parse(s) if err != nil { return nil, fmt.Errorf("uuid_to_hex_string: invalid UUID %q: %w", s, err) } var sb strings.Builder for _, b := range id[:] { fmt.Fprintf(&sb, "\\x%02x", b) } return sb.String(), nil }, "base64_to_hex_string": func(value any, err error) (any, error) { if err != nil { return value, err } s := fmt.Sprintf("%v", value) decoded, decodeErr := base64.StdEncoding.DecodeString(s) if decodeErr != nil { if id, parseErr := uuid.Parse(s); parseErr == nil { decoded = id[:] } else { return nil, fmt.Errorf("base64_to_hex_string: invalid base64 %q: %w", s, decodeErr) } } var sb strings.Builder for _, b := range decoded { fmt.Fprintf(&sb, "\\x%02x", b) } return sb.String(), nil }, }
var StepModules []func(ctx *godog.ScenarioContext, s *TestScenario)
StepModules is the list of functions used to register steps with a godog.ScenarioContext.
Functions ¶
func ApplyReportOptions ¶
ApplyReportOptions configures report output when GODOG_REPORT_DIR is set. GODOG_REPORT_FORMAT selects the report format (default: junit). When set to "cucumber", reports are written as JSON. Pass t.Name() as testName; slashes are replaced with dashes to form the filename. Returns a cleanup function that must be called (or deferred) after the test runs.
func DefaultOptions ¶
Types ¶
type Encoding ¶
type Encoding struct {
Name string
Marshal func(any) ([]byte, error)
Unmarshal func([]byte, any) error
}
Encoding wraps marshal/unmarshal for a specific format.
type ScenarioSetupFunc ¶
type ScenarioSetupFunc func(ctx context.Context, s *TestScenario, sc *godog.Scenario) (func(context.Context) error, error)
ScenarioSetupFunc provisions scenario-local runtime state and returns an optional cleanup callback.
type TaskRow ¶
type TaskRow struct {
ID string
TaskType string
TaskBody string
RetryAt time.Time
RetryCount int
LastError *string
}
TaskRow represents a row from the tasks table/collection.
type TestDB ¶
type TestDB interface {
// ClearAll wipes all data (called before each scenario).
ClearAll(ctx context.Context) error
// ResolveGroupID returns the conversation_group_id for a given conversation ID.
ResolveGroupID(ctx context.Context, conversationID string) (string, error)
// SetConversationEntriesCreatedAt forces all entries in a conversation group to the same created_at timestamp.
SetConversationEntriesCreatedAt(ctx context.Context, conversationID string, createdAt time.Time) error
// ExecSQL runs a raw SQL query and returns rows as maps. Non-SQL backends return (nil, nil) to skip assertions.
ExecSQL(ctx context.Context, query string) ([]map[string]interface{}, error)
// ExecMongoQuery runs a MongoDB query spec (JSON payload) and returns rows as maps.
// Non-Mongo backends return (nil, nil) to skip assertions.
ExecMongoQuery(ctx context.Context, query string) ([]map[string]interface{}, error)
// ArchiveConversation sets archived_at on the conversation and its group to `days` ago.
ArchiveConversation(ctx context.Context, conversationID string, days int) error
// ArchiveConversationOnly sets archived_at on the conversation only, without touching the group.
ArchiveConversationOnly(ctx context.Context, conversationID string, days int) error
// Task queue operations for task-queue BDD feature.
DeleteAllTasks(ctx context.Context) error
CreateTask(ctx context.Context, id, taskType, body string) error
CreateFailedTask(ctx context.Context, id, taskType, body string) error
ClaimReadyTasks(ctx context.Context, limit int) ([]TaskRow, error)
DeleteTask(ctx context.Context, id string) error
FailTask(ctx context.Context, id, errMsg string) error
GetTask(ctx context.Context, id string) (*TaskRow, error)
CountTasks(ctx context.Context) (int, error)
}
TestDB abstracts direct database operations needed by BDD steps, so different backends (Postgres, MongoDB) can be injected by the test runner.
type TestScenario ¶
type TestScenario struct {
Suite *TestSuite
ScenarioUID string
CurrentUser string
PathPrefix string
APIURL string
DB TestDB
Extra map[string]interface{}
Variables map[string]interface{}
Users map[string]*TestUser
// contains filtered or unexported fields
}
TestScenario holds state for a single scenario. Not accessed concurrently.
func (*TestScenario) APIBaseURL ¶
func (s *TestScenario) APIBaseURL() string
func (*TestScenario) EncodingMustMatch ¶
func (s *TestScenario) EncodingMustMatch(encoding Encoding, actual, expected string, expandExpected bool) error
func (*TestScenario) Expand ¶
func (s *TestScenario) Expand(value string, skippedVars ...string) (result string, rerr error)
Expand replaces ${var} in the string based on scenario variables.
func (*TestScenario) FilterQueryRows ¶
func (s *TestScenario) FilterQueryRows(rows []map[string]interface{}) []map[string]interface{}
func (*TestScenario) IsolatedClientID ¶
func (s *TestScenario) IsolatedClientID(clientID string) string
func (*TestScenario) IsolatedUser ¶
func (s *TestScenario) IsolatedUser(name string) string
func (*TestScenario) JSONMustContain ¶
func (s *TestScenario) JSONMustContain(actual, expected string, expand bool) error
func (*TestScenario) JSONMustMatch ¶
func (s *TestScenario) JSONMustMatch(actual, expected string, expandExpected bool) error
func (*TestScenario) NormalizeResponseBody ¶
func (s *TestScenario) NormalizeResponseBody(requestURL, contentType string, body []byte) ([]byte, error)
func (*TestScenario) NormalizeUsers ¶
func (s *TestScenario) NormalizeUsers(text string) string
func (*TestScenario) NormalizeValue ¶
func (s *TestScenario) NormalizeValue(value interface{}) interface{}
func (*TestScenario) RegisterCanonicalUsers ¶
func (s *TestScenario) RegisterCanonicalUsers(names ...string)
func (*TestScenario) Resolve ¶
func (s *TestScenario) Resolve(name string) (interface{}, error)
func (*TestScenario) ResolveString ¶
func (s *TestScenario) ResolveString(name string) (string, error)
func (*TestScenario) RewriteQuotedUsers ¶
func (s *TestScenario) RewriteQuotedUsers(text string) string
func (*TestScenario) RewriteRequestBody ¶
func (s *TestScenario) RewriteRequestBody(body string) string
func (*TestScenario) RewriteRequestPath ¶
func (s *TestScenario) RewriteRequestPath(path string) string
func (*TestScenario) SelectChild ¶
func (s *TestScenario) SelectChild(value any, path string) (any, error)
func (*TestScenario) SendHTTPRequestWithJSONBody ¶
func (s *TestScenario) SendHTTPRequestWithJSONBody(method, path string, jsonTxt *godog.DocString) error
func (*TestScenario) SendHTTPRequestWithJSONBodyAndStyle ¶
func (*TestScenario) SendHTTPRequestWithJSONBodyAsEventStream ¶
func (s *TestScenario) SendHTTPRequestWithJSONBodyAsEventStream(method, path string, jsonTxt *godog.DocString) error
func (*TestScenario) Session ¶
func (s *TestScenario) Session() *TestSession
func (*TestScenario) TestDB ¶
func (s *TestScenario) TestDB() TestDB
func (*TestScenario) TheResponseShouldContainJSONDoc ¶
func (s *TestScenario) TheResponseShouldContainJSONDoc(expected *godog.DocString) error
func (*TestScenario) TheResponseShouldMatchJSONDoc ¶
func (s *TestScenario) TheResponseShouldMatchJSONDoc(expected *godog.DocString) error
func (*TestScenario) User ¶
func (s *TestScenario) User() *TestUser
type TestSession ¶
type TestSession struct {
TestUser *TestUser
Client *http.Client
Resp *http.Response
RespBytes []byte
Header http.Header
EventStream bool
EventStreamEvents chan interface{}
// contains filtered or unexported fields
}
TestSession holds the HTTP context for a user, like a browser.
func (*TestSession) RespJSON ¶
func (s *TestSession) RespJSON() (interface{}, error)
RespJSON returns the last HTTP response body as parsed JSON.
func (*TestSession) SetRespBytes ¶
func (s *TestSession) SetRespBytes(bytes []byte)
type TestSuite ¶
type TestSuite struct {
Context interface{} // opaque application context
APIURL string
Mu sync.Mutex
TestingT *testing.T
Extra map[string]interface{} // additional test-scoped objects (e.g. mock servers)
DB TestDB // injected by test runner for store-specific operations
// ScenarioSetup optionally provisions per-scenario runtime state such as
// dedicated servers, datastore handles, and extra values.
ScenarioSetup ScenarioSetupFunc
}
TestSuite holds state global to all test scenarios. Accessed concurrently from all test scenarios.
func NewTestSuite ¶
func NewTestSuite() *TestSuite
func (*TestSuite) InitializeScenario ¶
func (suite *TestSuite) InitializeScenario(ctx *godog.ScenarioContext)