Skip to content

Commit

Permalink
Merge pull request #10 from pawelWritesCode/v2.1.0
Browse files Browse the repository at this point in the history
feature: Added new methods, updated gdutils & godog dependency, added…
  • Loading branch information
pawelWritesCode authored Jun 16, 2022
2 parents 9f2e2a0 + cb8983c commit c08cd0f
Show file tree
Hide file tree
Showing 17 changed files with 154 additions and 75 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: godog-example-setup
name: godog-http-api

on:
push:
Expand Down
29 changes: 19 additions & 10 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ This project **cuts initial time** & allows bootstrap e2e test framework with **
utility methods** in just few steps. Just grab it and write tests right away!

Benefits:
* [35+ well-documented, coupled in logical groups steps](https://github.com/pawelWritesCode/godog-http-api/wiki/Steps) useful for testing HTTP(s) API,
* [40+ well-documented, coupled in logical groups steps](https://github.com/pawelWritesCode/godog-http-api/wiki/Steps) useful for testing HTTP(s) API,
* support for using templated values as in [text/template](https://pkg.go.dev/text/template) package,
* support for querying nodes with different path engines ([oliveagle](https://github.com/oliveagle/jsonpath), [qjson](https://github.com/pawelWritesCode/qjson) - JSON, [go-yaml](https://github.com/goccy/go-yaml) - YAML, [antchfx](https://github.com/antchfx/xmlquery) - XML),
* support for querying nodes with different path engines ([oliveagle](https://github.com/oliveagle/jsonpath), [gjson](https://github.com/tidwall/gjson) - JSON, [go-yaml](https://github.com/goccy/go-yaml) - YAML, [antchfx](https://github.com/antchfx/xmlquery) - XML),
* support for sending _multipart/form-data_ forms with file in it,
* developed with debugging in mind,
* customisable through ability to [replace](https://github.com/pawelWritesCode/godog-http-api/blob/main/main_test.go#L53) utility services with your own implementations,
Expand All @@ -41,7 +41,7 @@ Feature: Adding new user
Given I generate a random word having from "5" to "10" of "ASCII" characters and save it as "RANDOM_FIRST_NAME"
Given I generate a random word having from "3" to "7" of "UNICODE" characters and save it as "RANDOM_LAST_NAME"
Given I generate a random sentence having from "3" to "4" of "english" words and save it as "RANDOM_DESCRIPTION"
Given I generate a random "int" in the range from "18" to "48" and save it as "RANDOM_AGE"
Given I generate a random "int" in the range from "18" to "20" and save it as "RANDOM_AGE"
Given I generate current time and travel "backward" "240h" in time and save it as "MEET_DATE"
Given I save "application/json" as "CONTENT_TYPE_JSON"
Expand Down Expand Up @@ -77,21 +77,19 @@ Feature: Adding new user
But the response body should have format "JSON"
And time between last request and response should be less than or equal to "2s"
# uncommenting next line will print last HTTP(s) response body to console
# uncommenting next line will print data to console
# Given I print last response body
# Given I print cache data
# This waiting is unnecessary, just added for demonstration
And I wait "2ms"
#---------------------------------------------------------------------------------------------------
# We validate response body with schema from assets/test_server/doc/schema/user/user.json
# step argument may be: relative (see .env variable GODOG_JSON_SCHEMA_DIR)
# step argument may be: relative (see .env variable GODOG_JSON_SCHEMA_DIR), full OS path, URL or raw schema definition
And the response body should be valid according to schema "user/user.json"
# or full OS path
And the response body should be valid according to schema "{{.CWD}}/assets/test_server/doc/schema/user/user.json"
# or URL pointing at schema
And the response body should be valid according to schema "https://raw.githubusercontent.com/pawelWritesCode/godog-http-api/main/assets/test_server/doc/schema/user/user.json"
# or raw schema definition passed in Docstring
And the response body should be valid according to schema:
"""
{
Expand All @@ -110,17 +108,28 @@ Feature: Adding new user
"type": "string"
}
"""
# here is used qjson "json-path" syntax to find JSON node
# here are used two different json-path engines (gjson & oliveagle) to find JSON nodes
And the "JSON" node "firstName" should be "string" of value "{{.RANDOM_FIRST_NAME}}"
# here is used oliveagle "json-path" syntax to find JSON node
And the "JSON" node "$.lastName" should be "string" of value "doe-{{.RANDOM_LAST_NAME}}"
# here we look for substrings
And the "JSON" node "$.lastName" should not contain sub string "smith"
But the "JSON" node "$.lastName" should contain sub string "doe"
# here is used regExp acceptable by standard go package "regExp"
And the "JSON" node "lastName" should not match regExp "smith-.*"
But the "JSON" node "lastName" should match regExp "doe-.*"
# assertion may be based on one of JSON data types: array, boolean, null, number, object
And the "JSON" node "age" should not be "string"
But the "JSON" node "$.age" should be "number"
And the "JSON" node "$.age" should be "number" and contain one of values "18, 19, 20"
# or on one of Go-like data types: bool, float, int, map, slice, string
But the "JSON" node "$.age" should be "int"
And the "JSON" node "age" should be "int" of value "{{.RANDOM_AGE}}"
And the "JSON" node "description" should be "string" of value "{{.RANDOM_DESCRIPTION}}"
# here date is formatted according to one of available formats from standard go package "time"
And the "JSON" node "friendSince" should be "string" of value "{{.MEET_DATE.Format `2006-01-02T15:04:05Z`}}"
```
Expand Down
55 changes: 49 additions & 6 deletions defs/scenario.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/pawelWritesCode/gdutils/pkg/format"
"github.com/pawelWritesCode/gdutils/pkg/stringutils"
"github.com/pawelWritesCode/gdutils/pkg/timeutils"
"github.com/pawelWritesCode/gdutils/pkg/types"
)

// Scenario is entity that contains utility services and holds methods used behind godog steps.
Expand All @@ -36,8 +37,12 @@ func (s *Scenario) IGenerateARandomRunesOfLengthWithCharactersAndSaveItAs(from,
generateWordFunc = s.APIContext.GeneratorRandomRunes(stringutils.CharsetEnglish)
case "russian":
generateWordFunc = s.APIContext.GeneratorRandomRunes(stringutils.CharsetRussian)
case "japanese":
generateWordFunc = s.APIContext.GeneratorRandomRunes(stringutils.CharsetJapanese)
case "emoji":
generateWordFunc = s.APIContext.GeneratorRandomRunes(stringutils.CharsetEmoji)
default:
return fmt.Errorf("unknown charset '%s', available: ascii, unicode, polish, english, russian", charset)
return fmt.Errorf("unknown charset '%s', available: ascii, unicode, polish, english, russian, japanese, emoji", charset)
}

return generateWordFunc(from, to, cacheKey)
Expand Down Expand Up @@ -72,8 +77,12 @@ func (s *Scenario) IGenerateARandomSentenceInTheRangeFromToWordsAndSaveItAs(minW
generateSentenceFunc = s.APIContext.GeneratorRandomSentence(stringutils.CharsetEnglish, minWordLength, maxWordLength)
case "russian":
generateSentenceFunc = s.APIContext.GeneratorRandomSentence(stringutils.CharsetRussian, minWordLength, maxWordLength)
case "japanese":
generateSentenceFunc = s.APIContext.GeneratorRandomSentence(stringutils.CharsetJapanese, minWordLength, maxWordLength)
case "emoji":
generateSentenceFunc = s.APIContext.GeneratorRandomSentence(stringutils.CharsetEmoji, minWordLength, maxWordLength)
default:
return fmt.Errorf("unknown charset '%s', available: ascii, unicode, polish, english, russian", charset)
return fmt.Errorf("unknown charset '%s', available: ascii, unicode, polish, english, russian, japanese, emoji", charset)
}

return generateSentenceFunc(from, to, cacheKey)
Expand Down Expand Up @@ -180,11 +189,28 @@ func (s *Scenario) TheResponseShouldOrShouldNotHaveNode(dataFormat, not, exprTem
return s.APIContext.AssertNodeExists(format.DataFormat(dataFormat), exprTemplate)
}

// TheNodeShouldBeOfValue compares json node value from expression to expected by user dataValue of given by user dataType
// TheNodeShouldBeOfValue compares node value from expression to expected by user dataValue of given by user dataType
// Available data types are listed in switch section in each case directive.
// expr should be valid according to injected PathFinder for provided dataFormat.
func (s *Scenario) TheNodeShouldBeOfValue(dataFormat, exprTemplate, dataType, dataValue string) error {
return s.APIContext.AssertNodeIsTypeAndValue(format.DataFormat(dataFormat), exprTemplate, dataType, dataValue)
return s.APIContext.AssertNodeIsTypeAndValue(format.DataFormat(dataFormat), exprTemplate, types.DataType(dataType), dataValue)
}

// TheNodeShouldBeOfValues compares node value from expression to expected by user one of values of given by user dataType
// Available data types are listed in switch section in each case directive.
// expr should be valid according to injected PathFinder for provided dataFormat.
func (s *Scenario) TheNodeShouldBeOfValues(dataFormat, exprTemplate, dataType, valuesTemplates string) error {
return s.APIContext.AssertNodeIsTypeAndHasOneOfValues(format.DataFormat(dataFormat), exprTemplate, types.DataType(dataType), valuesTemplates)
}

// TheNodeShouldOrShouldNotContainSubString checks whether value of last HTTP response node, obtained using exprTemplate
// is string type and contains/doesn't contain given substring
func (s *Scenario) TheNodeShouldOrShouldNotContainSubString(dataFormat, exprTemplate, not, subTemplate string) error {
if len(not) > 0 {
return s.APIContext.AssertNodeNotContainsSubString(format.DataFormat(dataFormat), exprTemplate, subTemplate)
}

return s.APIContext.AssertNodeContainsSubString(format.DataFormat(dataFormat), exprTemplate, subTemplate)
}

// TheNodeShouldOrShouldNotBeSliceOfLength checks whether given key is slice and has/hasn't given length
Expand All @@ -202,10 +228,10 @@ func (s *Scenario) TheNodeShouldOrShouldNotBeSliceOfLength(dataFormat, exprTempl
// expr should be valid according to injected PathResolver
func (s *Scenario) TheNodeShouldOrShouldNotBe(dataFormat, exprTemplate, not, goType string) error {
if len(not) > 0 {
return s.APIContext.AssertNodeIsNotType(format.DataFormat(dataFormat), exprTemplate, goType)
return s.APIContext.AssertNodeIsNotType(format.DataFormat(dataFormat), exprTemplate, types.DataType(goType))
}

return s.APIContext.AssertNodeIsType(format.DataFormat(dataFormat), exprTemplate, goType)
return s.APIContext.AssertNodeIsType(format.DataFormat(dataFormat), exprTemplate, types.DataType(goType))
}

// TheResponseShouldHaveNodes checks whether last request body has keys defined in string separated by comma
Expand Down Expand Up @@ -277,6 +303,16 @@ func (s *Scenario) TheResponseShouldHaveCookieOfValue(name, valueTemplate string
return s.APIContext.AssertResponseCookieValueIs(name, valueTemplate)
}

// TheResponseCookieShouldOrShouldNotMatchRegExp checks whether last HTTP(s) response has cookie of given name and value
// matches/doesn't match provided regExp.
func (s *Scenario) TheResponseCookieShouldOrShouldNotMatchRegExp(name, not, regExpTemplate string) error {
if len(not) > 0 {
return s.APIContext.AssertResponseCookieValueNotMatchesRegExp(name, regExpTemplate)
}

return s.APIContext.AssertResponseCookieValueMatchesRegExp(name, regExpTemplate)
}

// IValidateNodeWithSchemaReference validates last response body node against schema as provided in reference
func (s *Scenario) IValidateNodeWithSchemaReference(dataFormat, exprTemplate, referenceTemplate string) error {
return s.APIContext.AssertNodeMatchesSchemaByReference(format.DataFormat(dataFormat), exprTemplate, referenceTemplate)
Expand Down Expand Up @@ -307,6 +343,13 @@ func (s *Scenario) IPrintLastResponseBody() error {
return s.APIContext.DebugPrintResponseBody()
}

// IPrintCacheData prints all current scenario cache data.
func (s *Scenario) IPrintCacheData() error {
fmt.Printf("%#v", s.APIContext.Cache.All())

return nil
}

/*
IWait waits for provided time interval amount of time
timeInterval should be string valid for time.ParseDuration func,
Expand Down
25 changes: 17 additions & 8 deletions features/test_server/json/create.feature
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Feature: Adding new user
Given I generate a random word having from "5" to "10" of "ASCII" characters and save it as "RANDOM_FIRST_NAME"
Given I generate a random word having from "3" to "7" of "UNICODE" characters and save it as "RANDOM_LAST_NAME"
Given I generate a random sentence having from "3" to "4" of "english" words and save it as "RANDOM_DESCRIPTION"
Given I generate a random "int" in the range from "18" to "48" and save it as "RANDOM_AGE"
Given I generate a random "int" in the range from "18" to "20" and save it as "RANDOM_AGE"
Given I generate current time and travel "backward" "240h" in time and save it as "MEET_DATE"
Given I save "application/json" as "CONTENT_TYPE_JSON"

Expand Down Expand Up @@ -50,21 +50,19 @@ Feature: Adding new user
But the response body should have format "JSON"
And time between last request and response should be less than or equal to "2s"

# uncommenting next line will print last HTTP(s) response body to console
# uncommenting next line will print data to console
# Given I print last response body
# Given I print cache data

# This waiting is unnecessary, just added for demonstration
And I wait "2ms"

#---------------------------------------------------------------------------------------------------
# We validate response body with schema from assets/test_server/doc/schema/user/user.json
# step argument may be: relative (see .env variable GODOG_JSON_SCHEMA_DIR)
# step argument may be: relative (see .env variable GODOG_JSON_SCHEMA_DIR), full OS path, URL or raw schema definition
And the response body should be valid according to schema "user/user.json"
# or full OS path
And the response body should be valid according to schema "{{.CWD}}/assets/test_server/doc/schema/user/user.json"
# or URL pointing at schema
And the response body should be valid according to schema "https://raw.githubusercontent.com/pawelWritesCode/godog-http-api/main/assets/test_server/doc/schema/user/user.json"
# or raw schema definition passed in Docstring
And the response body should be valid according to schema:
"""
{
Expand All @@ -83,17 +81,28 @@ Feature: Adding new user
"type": "string"
}
"""
# here is used qjson "json-path" syntax to find JSON node
# here are used two different json-path engines (gjson & oliveagle) to find JSON nodes
And the "JSON" node "firstName" should be "string" of value "{{.RANDOM_FIRST_NAME}}"
# here is used oliveagle "json-path" syntax to find JSON node
And the "JSON" node "$.lastName" should be "string" of value "doe-{{.RANDOM_LAST_NAME}}"

# here we look for substrings
And the "JSON" node "$.lastName" should not contain sub string "smith"
But the "JSON" node "$.lastName" should contain sub string "doe"

# here is used regExp acceptable by standard go package "regExp"
And the "JSON" node "lastName" should not match regExp "smith-.*"
But the "JSON" node "lastName" should match regExp "doe-.*"

# assertion may be based on one of JSON data types: array, boolean, null, number, object
And the "JSON" node "age" should not be "string"
But the "JSON" node "$.age" should be "number"
And the "JSON" node "$.age" should be "number" and contain one of values "18, 19, 20"

# or on one of Go-like data types: bool, float, int, map, slice, string
But the "JSON" node "$.age" should be "int"
And the "JSON" node "age" should be "int" of value "{{.RANDOM_AGE}}"
And the "JSON" node "description" should be "string" of value "{{.RANDOM_DESCRIPTION}}"

# here date is formatted according to one of available formats from standard go package "time"
And the "JSON" node "friendSince" should be "string" of value "{{.MEET_DATE.Format `2006-01-02T15:04:05Z`}}"

Expand Down
7 changes: 4 additions & 3 deletions features/test_server/json/get_many.feature
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Feature: Fetching many users.
And the response should have header "Content-Type" of value "application/json; charset=UTF-8"
And the response body should have format "JSON"
# here we only check only node type, not its exact value
And the "JSON" node "root" should be "slice"
But the "JSON" node "root" should not be slice of length "0"
And the "JSON" node "root" should not be "nil"
And the "JSON" node "@this" should be "slice"
But the "JSON" node "@this" should not be slice of length "0"
And the "JSON" node "@this" should not be "nil"
And the "JSON" node "@this" should not be "null"
4 changes: 2 additions & 2 deletions features/test_server/json/get_one.feature
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ Feature: Fetching single user.
And the response body should be valid according to schema "user/user.json"
And the "JSON" node "firstName" should be "string" of value "{{.RANDOM_FIRST_NAME}}"
And the "JSON" node "lastName" should be "string" of value "{{.RANDOM_LAST_NAME}}"
And the "JSON" node "age" should be "int" of value "{{.RANDOM_AGE}}"
And the "JSON" node "id" should be "int" of value "{{.USER_ID}}"
And the "JSON" node "age" should be "number" of value "{{.RANDOM_AGE}}"
And the "JSON" node "id" should be "number" of value "{{.USER_ID}}"
And the "JSON" node "description" should be "string" of value "{{.RANDOM_DESCRIPTION}}"
And the "JSON" node "friendSince" should be "string" of value "{{.MEET_DATE.Format `2006-01-02T15:04:05Z`}}"

Expand Down
12 changes: 6 additions & 6 deletions features/test_server/json/replace.feature
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ Feature: Replacing single user account.
And the response body should be valid according to schema "user/user.json"
And the "JSON" node "firstName" should be "string" of value "{{.RANDOM_FIRST_NAME}}"
And the "JSON" node "lastName" should be "string" of value "{{.RANDOM_LAST_NAME}}"
And the "JSON" node "age" should be "int" of value "{{.RANDOM_AGE}}"
And the "JSON" node "id" should be "int" of value "{{.USER_ID}}"
And the "JSON" node "age" should be "number" of value "{{.RANDOM_AGE}}"
And the "JSON" node "id" should be "number" of value "{{.USER_ID}}"
And the "JSON" node "description" should be "string" of value "{{.RANDOM_DESCRIPTION}}"
And the "JSON" node "friendSince" should be "string" of value "{{.MEET_DATE.Format `2006-01-02T15:04:05Z`}}"

Expand Down Expand Up @@ -107,8 +107,8 @@ Feature: Replacing single user account.
And the response body should be valid according to schema "user/user.json"
And the "JSON" node "firstName" should be "string" of value "{{.NEW_USER_RANDOM_FIRST_NAME}}"
And the "JSON" node "lastName" should be "string" of value "{{.NEW_USER_RANDOM_LAST_NAME}}"
And the "JSON" node "age" should be "int" of value "{{.NEW_USER_RANDOM_AGE}}"
And the "JSON" node "id" should be "int" of value "{{.USER_ID}}"
And the "JSON" node "age" should be "number" of value "{{.NEW_USER_RANDOM_AGE}}"
And the "JSON" node "id" should be "number" of value "{{.USER_ID}}"
And the "JSON" node "description" should be "string" of value "{{.NEW_USER_RANDOM_DESCRIPTION}}"
And the "JSON" node "friendSince" should be "string" of value "{{.NEW_USER_MEET_DATE.Format `2006-01-02T15:04:05Z`}}"

Expand Down Expand Up @@ -185,8 +185,8 @@ Feature: Replacing single user account.
And the response body should be valid according to schema "user/user.json"
And the "JSON" node "firstName" should be "string" of value "{{.RANDOM_FIRST_NAME}}"
And the "JSON" node "lastName" should be "string" of value "{{.RANDOM_LAST_NAME}}"
And the "JSON" node "age" should be "int" of value "{{.RANDOM_AGE}}"
And the "JSON" node "id" should be "int" of value "{{.USER_ID}}"
And the "JSON" node "age" should be "number" of value "{{.RANDOM_AGE}}"
And the "JSON" node "id" should be "number" of value "{{.USER_ID}}"
And the "JSON" node "description" should be "string" of value "{{.RANDOM_DESCRIPTION}}"
And the "JSON" node "friendSince" should be "string" of value "{{.MEET_DATE.Format `2006-01-02T15:04:05Z`}}"

Expand Down
2 changes: 1 addition & 1 deletion features/test_server/xml/create.feature
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Feature: Adding new user
And the "XML" node "//firstName" should be "string" of value "{{.RANDOM_FIRST_NAME}}"
And the "XML" node "//lastName" should be "string" of value "doe-{{.RANDOM_LAST_NAME}}"
And the "XML" node "//lastName" should match regExp "doe-.*"
And the "XML" node "//age" should be "int" of value "{{.RANDOM_AGE}}"
And the "XML" node "//age" should be "integer" of value "{{.RANDOM_AGE}}"
And the "XML" node "//description" should be "string" of value "{{.RANDOM_DESCRIPTION}}"
And the "XML" node "//friendSince" should be "string" of value "{{.MEET_DATE.Format `2006-01-02T15:04:05Z`}}"

Expand Down
Loading

0 comments on commit c08cd0f

Please # to comment.