Skip to content
This repository has been archived by the owner on Feb 27, 2023. It is now read-only.

Commit

Permalink
Add example for dynamic vars (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
vearutop authored Aug 15, 2021
1 parent ff0503f commit cb8f942
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 25 deletions.
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,58 @@ _testdata/sample.json5
"""
```

### Dynamic Variables

When data is not known in advance, but can be inferred from previous steps, you can use
[dynamic variables](https://github.com/bool64/shared).

Here is an example where value from response of one step is used in request of another step.

```gherkin
Scenario: Creating user and making an order
When I request HTTP endpoint with method "POST" and URI "/user"
And I request HTTP endpoint with body
"""json
{"name": "John Doe"}
"""
# Undefined variable infers its value from the actual data on first encounter.
Then I should have response with body
"""json5
{
// Capturing dynamic user id as $user_id variable.
"id":"$user_id",
"name": "John Doe",
// Ignoring other dynamic values.
"created_at":"<ignore-diff>","updated_at": "<ignore-diff>"
}
"""
# Creating an order for that user with $user_id.
When I request HTTP endpoint with method "POST" and URI "/order"
And I request HTTP endpoint with body
"""json5
{
// Replacing with the value of a variable captured previously.
"user_id": "$user_id",
"item_name": "Watermelon"
}
"""
# Variable interpolation works also with body from file.
Then I should have response with body
"""json5
{
"id":"<ignore-diff>",
"created_at":"<ignore-diff>","updated_at": "<ignore-diff>",
"user_id":"$user_id"
}
"""
```


## Example Feature

```gherkin
Expand Down
43 changes: 43 additions & 0 deletions _testdata/Dynamic.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Feature: Dynamic data is used in steps

Scenario: Creating user and making an order
When I request HTTP endpoint with method "POST" and URI "/user"

And I request HTTP endpoint with body
"""json
{"name": "John Doe"}
"""

# Undefined variable infers its value from the actual data on first encounter.
Then I should have response with body
"""json5
{
// Capturing dynamic user id as $user_id variable.
"id":"$user_id",
"name": "John Doe",
// Ignoring other dynamic values.
"created_at":"<ignore-diff>","updated_at": "<ignore-diff>"
}
"""

# Creating an order for that user with $user_id.
When I request HTTP endpoint with method "POST" and URI "/order"

And I request HTTP endpoint with body
"""json5
{
// Replacing with the value of a variable captured previously.
"user_id": "$user_id",
"item_name": "Watermelon"
}
"""
# Variable interpolation works also with body from file.

Then I should have response with body
"""json5
{
"id":"<ignore-diff>",
"created_at":"<ignore-diff>","updated_at": "<ignore-diff>",
"user_id":"$user_id"
}
"""
8 changes: 4 additions & 4 deletions _testdata/External.feature
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ Feature: External Services
And "some-service" response includes header "X-Bar: foo"

And "some-service" responds with status "OK" and body
"""
"""json
{"key":"value"}
"""

Given "another-service" receives "POST" request "/post-something" with body
"""
"""json5
// Could be a JSON5 too.
{"foo":"bar"}
"""
Expand All @@ -23,7 +23,7 @@ Feature: External Services
And "another-service" request is received several times

And "another-service" responds with status "OK" and body
"""
"""json
{"theFooWas":"bar"}
"""

Expand All @@ -34,7 +34,7 @@ Feature: External Services
Given "some-service" receives "GET" request "/ask-for-foo"

And "some-service" responds with status "OK" and body
"""
"""json
"foo"
"""

Expand Down
8 changes: 4 additions & 4 deletions _testdata/Local.feature
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Feature: HTTP Service
Then I should have response with status "OK"

And I should have response with body
"""
"""json
[
{"some":"json"}
]
Expand Down Expand Up @@ -41,7 +41,7 @@ Feature: HTTP Service
"""

Then I should have response with body
"""
"""json
{"status":"ok"}
"""

Expand All @@ -63,7 +63,7 @@ Feature: HTTP Service
And I should have other responses with status "Not Found"

And I should have other responses with body
"""
"""json
{"status":"failed"}
"""

Expand All @@ -73,7 +73,7 @@ Feature: HTTP Service
When I request HTTP endpoint with method "POST" and URI "/with-json5-body"

And I request HTTP endpoint with body
"""
"""json5
[
// some test data
{"some":"json5"}
Expand Down
8 changes: 4 additions & 4 deletions external.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ func (e *External) serviceRequestIncludesHeader(service, header, value string) e
}

func (e *External) serviceReceivesRequestWithBody(service, method, requestURI string, bodyDoc *godog.DocString) error {
body, err := loadBody([]byte(bodyDoc.Content))
body, err := loadBody([]byte(bodyDoc.Content), e.Vars)
if err != nil {
return err
}
Expand All @@ -226,7 +226,7 @@ func (e *External) serviceReceivesRequestWithBody(service, method, requestURI st
}

func (e *External) serviceReceivesRequestWithBodyFromFile(service, method, requestURI string, filePath *godog.DocString) error {
body, err := loadBodyFromFile(filePath.Content)
body, err := loadBodyFromFile(filePath.Content, e.Vars)
if err != nil {
return err
}
Expand Down Expand Up @@ -356,7 +356,7 @@ func (e *External) serviceResponseIncludesHeader(service, header, value string)
}

func (e *External) serviceRespondsWithStatusAndBody(service, statusOrCode string, bodyDoc *godog.DocString) error {
body, err := loadBody([]byte(bodyDoc.Content))
body, err := loadBody([]byte(bodyDoc.Content), e.Vars)
if err != nil {
return err
}
Expand All @@ -365,7 +365,7 @@ func (e *External) serviceRespondsWithStatusAndBody(service, statusOrCode string
}

func (e *External) serviceRespondsWithStatusAndBodyFromFile(service, statusOrCode string, filePath *godog.DocString) error {
body, err := loadBodyFromFile(filePath.Content)
body, err := loadBodyFromFile(filePath.Content, e.Vars)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ require (
github.com/bool64/shared v0.1.3
github.com/cucumber/godog v0.11.0
github.com/stretchr/testify v1.7.0
github.com/swaggest/assertjson v1.6.6
github.com/swaggest/assertjson v1.6.7
github.com/swaggest/rest v0.2.11
)
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/swaggest/assertjson v1.6.6 h1:QO//07sfx9KR6h4t/8L+I5qOWUifLnu8gblhFYwkVQM=
github.com/swaggest/assertjson v1.6.6/go.mod h1:HNfIAKxJJkeDmtrBU01dcWvEFjhghO96gx9Eo4rRjIc=
github.com/swaggest/assertjson v1.6.7 h1:Sge9pjgXtfgsH8e1oUK+MZRe8N0BlwyNxKww5Uddf8c=
github.com/swaggest/assertjson v1.6.7/go.mod h1:kYEgSuEhz5pLkx4A086f9voGIrJZJUweLLYpkaNYKQM=
github.com/swaggest/form/v5 v5.0.1/go.mod h1:vdnaSTze7cxVKhWiCabrfm1YeLwWLpb9P941Gxv4FnA=
github.com/swaggest/jsonschema-go v0.3.22/go.mod h1:UMMhyRN4FIUjy6ivVycElnWq+C7ql/1+L2R0BG01LkE=
github.com/swaggest/openapi-go v0.2.12/go.mod h1:vRXsxIkIg851HF0XiIeUQ5L2DMbag2oCpid9CUXRwbo=
Expand Down
44 changes: 33 additions & 11 deletions local.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package httpdog

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"strconv"
"strings"

"github.com/bool64/shared"
"github.com/cucumber/godog"
"github.com/swaggest/assertjson/json5"
"github.com/swaggest/rest/resttest"
Expand All @@ -21,9 +24,13 @@ func NewLocal(baseURL string) *Local {

baseURL = strings.TrimRight(baseURL, "/")

return &Local{
l := Local{
Client: resttest.NewClient(baseURL),
}

l.JSONComparer.Vars = &shared.Vars{}

return &l
}

// Local is step-driven HTTP client for application local HTTP service.
Expand Down Expand Up @@ -180,25 +187,40 @@ func (l *Local) iRequestWithMethodAndURI(method, uri string) error {
return nil
}

func loadBodyFromFile(filePath string) ([]byte, error) {
func loadBodyFromFile(filePath string, vars *shared.Vars) ([]byte, error) {
body, err := ioutil.ReadFile(filePath) // nolint:gosec // File inclusion via variable during tests.
if err != nil {
return nil, err
}

return loadBody(body)
return loadBody(body, vars)
}

func loadBody(body []byte) ([]byte, error) {
func loadBody(body []byte, vars *shared.Vars) ([]byte, error) {
var err error

if json5.Valid(body) {
return json5.Downgrade(body)
if body, err = json5.Downgrade(body); err != nil {
return nil, fmt.Errorf("failed to downgrade JSON5 to JSON: %w", err)
}
}

if vars != nil {
for k, v := range vars.GetAll() {
jv, err := json.Marshal(v)
if err != nil {
return nil, fmt.Errorf("failed to marshal var %s (%v): %w", k, v, err)
}

body = bytes.ReplaceAll(body, []byte(`"`+k+`"`), jv)
}
}

return body, nil
}

func (l *Local) iRequestWithBodyFromFile(filePath *godog.DocString) error {
body, err := loadBodyFromFile(filePath.Content)
body, err := loadBodyFromFile(filePath.Content, l.JSONComparer.Vars)

if err == nil {
l.WithBody(body)
Expand All @@ -208,7 +230,7 @@ func (l *Local) iRequestWithBodyFromFile(filePath *godog.DocString) error {
}

func (l *Local) iRequestWithBody(bodyDoc *godog.DocString) error {
body, err := loadBody([]byte(bodyDoc.Content))
body, err := loadBody([]byte(bodyDoc.Content), l.JSONComparer.Vars)

if err == nil {
l.WithBody(body)
Expand Down Expand Up @@ -281,7 +303,7 @@ func (l *Local) iShouldHaveResponseWithHeader(key, value string) error {
}

func (l *Local) iShouldHaveResponseWithBody(bodyDoc *godog.DocString) error {
body, err := loadBody([]byte(bodyDoc.Content))
body, err := loadBody([]byte(bodyDoc.Content), l.JSONComparer.Vars)
if err != nil {
return err
}
Expand All @@ -290,7 +312,7 @@ func (l *Local) iShouldHaveResponseWithBody(bodyDoc *godog.DocString) error {
}

func (l *Local) iShouldHaveResponseWithBodyFromFile(filePath *godog.DocString) error {
body, err := loadBodyFromFile(filePath.Content)
body, err := loadBodyFromFile(filePath.Content, l.JSONComparer.Vars)
if err != nil {
return err
}
Expand All @@ -299,7 +321,7 @@ func (l *Local) iShouldHaveResponseWithBodyFromFile(filePath *godog.DocString) e
}

func (l *Local) iShouldHaveOtherResponsesWithBody(bodyDoc *godog.DocString) error {
body, err := loadBody([]byte(bodyDoc.Content))
body, err := loadBody([]byte(bodyDoc.Content), l.JSONComparer.Vars)
if err != nil {
return err
}
Expand All @@ -308,7 +330,7 @@ func (l *Local) iShouldHaveOtherResponsesWithBody(bodyDoc *godog.DocString) erro
}

func (l *Local) iShouldHaveOtherResponsesWithBodyFromFile(filePath *godog.DocString) error {
body, err := loadBodyFromFile(filePath.Content)
body, err := loadBodyFromFile(filePath.Content, l.JSONComparer.Vars)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit cb8f942

Please sign in to comment.