Skip to content

Commit c08cd0f

Browse files
Merge pull request #10 from pawelWritesCode/v2.1.0
feature: Added new methods, updated gdutils & godog dependency, added…
2 parents 9f2e2a0 + cb8983c commit c08cd0f

File tree

17 files changed

+154
-75
lines changed

17 files changed

+154
-75
lines changed

.github/workflows/go.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: godog-example-setup
1+
name: godog-http-api
22

33
on:
44
push:

README.MD

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ This project **cuts initial time** & allows bootstrap e2e test framework with **
1212
utility methods** in just few steps. Just grab it and write tests right away!
1313

1414
Benefits:
15-
* [35+ well-documented, coupled in logical groups steps](https://github.com/pawelWritesCode/godog-http-api/wiki/Steps) useful for testing HTTP(s) API,
15+
* [40+ well-documented, coupled in logical groups steps](https://github.com/pawelWritesCode/godog-http-api/wiki/Steps) useful for testing HTTP(s) API,
1616
* support for using templated values as in [text/template](https://pkg.go.dev/text/template) package,
17-
* 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),
17+
* 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),
1818
* support for sending _multipart/form-data_ forms with file in it,
1919
* developed with debugging in mind,
2020
* customisable through ability to [replace](https://github.com/pawelWritesCode/godog-http-api/blob/main/main_test.go#L53) utility services with your own implementations,
@@ -41,7 +41,7 @@ Feature: Adding new user
4141
Given I generate a random word having from "5" to "10" of "ASCII" characters and save it as "RANDOM_FIRST_NAME"
4242
Given I generate a random word having from "3" to "7" of "UNICODE" characters and save it as "RANDOM_LAST_NAME"
4343
Given I generate a random sentence having from "3" to "4" of "english" words and save it as "RANDOM_DESCRIPTION"
44-
Given I generate a random "int" in the range from "18" to "48" and save it as "RANDOM_AGE"
44+
Given I generate a random "int" in the range from "18" to "20" and save it as "RANDOM_AGE"
4545
Given I generate current time and travel "backward" "240h" in time and save it as "MEET_DATE"
4646
Given I save "application/json" as "CONTENT_TYPE_JSON"
4747
@@ -77,21 +77,19 @@ Feature: Adding new user
7777
But the response body should have format "JSON"
7878
And time between last request and response should be less than or equal to "2s"
7979
80-
# uncommenting next line will print last HTTP(s) response body to console
80+
# uncommenting next line will print data to console
8181
# Given I print last response body
82+
# Given I print cache data
8283
8384
# This waiting is unnecessary, just added for demonstration
8485
And I wait "2ms"
8586
8687
#---------------------------------------------------------------------------------------------------
8788
# We validate response body with schema from assets/test_server/doc/schema/user/user.json
88-
# step argument may be: relative (see .env variable GODOG_JSON_SCHEMA_DIR)
89+
# step argument may be: relative (see .env variable GODOG_JSON_SCHEMA_DIR), full OS path, URL or raw schema definition
8990
And the response body should be valid according to schema "user/user.json"
90-
# or full OS path
9191
And the response body should be valid according to schema "{{.CWD}}/assets/test_server/doc/schema/user/user.json"
92-
# or URL pointing at schema
9392
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"
94-
# or raw schema definition passed in Docstring
9593
And the response body should be valid according to schema:
9694
"""
9795
{
@@ -110,17 +108,28 @@ Feature: Adding new user
110108
"type": "string"
111109
}
112110
"""
113-
# here is used qjson "json-path" syntax to find JSON node
111+
# here are used two different json-path engines (gjson & oliveagle) to find JSON nodes
114112
And the "JSON" node "firstName" should be "string" of value "{{.RANDOM_FIRST_NAME}}"
115-
# here is used oliveagle "json-path" syntax to find JSON node
116113
And the "JSON" node "$.lastName" should be "string" of value "doe-{{.RANDOM_LAST_NAME}}"
114+
115+
# here we look for substrings
116+
And the "JSON" node "$.lastName" should not contain sub string "smith"
117+
But the "JSON" node "$.lastName" should contain sub string "doe"
118+
117119
# here is used regExp acceptable by standard go package "regExp"
118120
And the "JSON" node "lastName" should not match regExp "smith-.*"
119121
But the "JSON" node "lastName" should match regExp "doe-.*"
122+
123+
# assertion may be based on one of JSON data types: array, boolean, null, number, object
120124
And the "JSON" node "age" should not be "string"
125+
But the "JSON" node "$.age" should be "number"
126+
And the "JSON" node "$.age" should be "number" and contain one of values "18, 19, 20"
127+
128+
# or on one of Go-like data types: bool, float, int, map, slice, string
121129
But the "JSON" node "$.age" should be "int"
122130
And the "JSON" node "age" should be "int" of value "{{.RANDOM_AGE}}"
123131
And the "JSON" node "description" should be "string" of value "{{.RANDOM_DESCRIPTION}}"
132+
124133
# here date is formatted according to one of available formats from standard go package "time"
125134
And the "JSON" node "friendSince" should be "string" of value "{{.MEET_DATE.Format `2006-01-02T15:04:05Z`}}"
126135
```

defs/scenario.go

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/pawelWritesCode/gdutils/pkg/format"
1313
"github.com/pawelWritesCode/gdutils/pkg/stringutils"
1414
"github.com/pawelWritesCode/gdutils/pkg/timeutils"
15+
"github.com/pawelWritesCode/gdutils/pkg/types"
1516
)
1617

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

4348
return generateWordFunc(from, to, cacheKey)
@@ -72,8 +77,12 @@ func (s *Scenario) IGenerateARandomSentenceInTheRangeFromToWordsAndSaveItAs(minW
7277
generateSentenceFunc = s.APIContext.GeneratorRandomSentence(stringutils.CharsetEnglish, minWordLength, maxWordLength)
7378
case "russian":
7479
generateSentenceFunc = s.APIContext.GeneratorRandomSentence(stringutils.CharsetRussian, minWordLength, maxWordLength)
80+
case "japanese":
81+
generateSentenceFunc = s.APIContext.GeneratorRandomSentence(stringutils.CharsetJapanese, minWordLength, maxWordLength)
82+
case "emoji":
83+
generateSentenceFunc = s.APIContext.GeneratorRandomSentence(stringutils.CharsetEmoji, minWordLength, maxWordLength)
7584
default:
76-
return fmt.Errorf("unknown charset '%s', available: ascii, unicode, polish, english, russian", charset)
85+
return fmt.Errorf("unknown charset '%s', available: ascii, unicode, polish, english, russian, japanese, emoji", charset)
7786
}
7887

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

183-
// TheNodeShouldBeOfValue compares json node value from expression to expected by user dataValue of given by user dataType
192+
// TheNodeShouldBeOfValue compares node value from expression to expected by user dataValue of given by user dataType
184193
// Available data types are listed in switch section in each case directive.
185194
// expr should be valid according to injected PathFinder for provided dataFormat.
186195
func (s *Scenario) TheNodeShouldBeOfValue(dataFormat, exprTemplate, dataType, dataValue string) error {
187-
return s.APIContext.AssertNodeIsTypeAndValue(format.DataFormat(dataFormat), exprTemplate, dataType, dataValue)
196+
return s.APIContext.AssertNodeIsTypeAndValue(format.DataFormat(dataFormat), exprTemplate, types.DataType(dataType), dataValue)
197+
}
198+
199+
// TheNodeShouldBeOfValues compares node value from expression to expected by user one of values of given by user dataType
200+
// Available data types are listed in switch section in each case directive.
201+
// expr should be valid according to injected PathFinder for provided dataFormat.
202+
func (s *Scenario) TheNodeShouldBeOfValues(dataFormat, exprTemplate, dataType, valuesTemplates string) error {
203+
return s.APIContext.AssertNodeIsTypeAndHasOneOfValues(format.DataFormat(dataFormat), exprTemplate, types.DataType(dataType), valuesTemplates)
204+
}
205+
206+
// TheNodeShouldOrShouldNotContainSubString checks whether value of last HTTP response node, obtained using exprTemplate
207+
// is string type and contains/doesn't contain given substring
208+
func (s *Scenario) TheNodeShouldOrShouldNotContainSubString(dataFormat, exprTemplate, not, subTemplate string) error {
209+
if len(not) > 0 {
210+
return s.APIContext.AssertNodeNotContainsSubString(format.DataFormat(dataFormat), exprTemplate, subTemplate)
211+
}
212+
213+
return s.APIContext.AssertNodeContainsSubString(format.DataFormat(dataFormat), exprTemplate, subTemplate)
188214
}
189215

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

208-
return s.APIContext.AssertNodeIsType(format.DataFormat(dataFormat), exprTemplate, goType)
234+
return s.APIContext.AssertNodeIsType(format.DataFormat(dataFormat), exprTemplate, types.DataType(goType))
209235
}
210236

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

306+
// TheResponseCookieShouldOrShouldNotMatchRegExp checks whether last HTTP(s) response has cookie of given name and value
307+
// matches/doesn't match provided regExp.
308+
func (s *Scenario) TheResponseCookieShouldOrShouldNotMatchRegExp(name, not, regExpTemplate string) error {
309+
if len(not) > 0 {
310+
return s.APIContext.AssertResponseCookieValueNotMatchesRegExp(name, regExpTemplate)
311+
}
312+
313+
return s.APIContext.AssertResponseCookieValueMatchesRegExp(name, regExpTemplate)
314+
}
315+
280316
// IValidateNodeWithSchemaReference validates last response body node against schema as provided in reference
281317
func (s *Scenario) IValidateNodeWithSchemaReference(dataFormat, exprTemplate, referenceTemplate string) error {
282318
return s.APIContext.AssertNodeMatchesSchemaByReference(format.DataFormat(dataFormat), exprTemplate, referenceTemplate)
@@ -307,6 +343,13 @@ func (s *Scenario) IPrintLastResponseBody() error {
307343
return s.APIContext.DebugPrintResponseBody()
308344
}
309345

346+
// IPrintCacheData prints all current scenario cache data.
347+
func (s *Scenario) IPrintCacheData() error {
348+
fmt.Printf("%#v", s.APIContext.Cache.All())
349+
350+
return nil
351+
}
352+
310353
/*
311354
IWait waits for provided time interval amount of time
312355
timeInterval should be string valid for time.ParseDuration func,

features/test_server/json/create.feature

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Feature: Adding new user
1414
Given I generate a random word having from "5" to "10" of "ASCII" characters and save it as "RANDOM_FIRST_NAME"
1515
Given I generate a random word having from "3" to "7" of "UNICODE" characters and save it as "RANDOM_LAST_NAME"
1616
Given I generate a random sentence having from "3" to "4" of "english" words and save it as "RANDOM_DESCRIPTION"
17-
Given I generate a random "int" in the range from "18" to "48" and save it as "RANDOM_AGE"
17+
Given I generate a random "int" in the range from "18" to "20" and save it as "RANDOM_AGE"
1818
Given I generate current time and travel "backward" "240h" in time and save it as "MEET_DATE"
1919
Given I save "application/json" as "CONTENT_TYPE_JSON"
2020

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

53-
# uncommenting next line will print last HTTP(s) response body to console
53+
# uncommenting next line will print data to console
5454
# Given I print last response body
55+
# Given I print cache data
5556

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

5960
#---------------------------------------------------------------------------------------------------
6061
# We validate response body with schema from assets/test_server/doc/schema/user/user.json
61-
# step argument may be: relative (see .env variable GODOG_JSON_SCHEMA_DIR)
62+
# step argument may be: relative (see .env variable GODOG_JSON_SCHEMA_DIR), full OS path, URL or raw schema definition
6263
And the response body should be valid according to schema "user/user.json"
63-
# or full OS path
6464
And the response body should be valid according to schema "{{.CWD}}/assets/test_server/doc/schema/user/user.json"
65-
# or URL pointing at schema
6665
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"
67-
# or raw schema definition passed in Docstring
6866
And the response body should be valid according to schema:
6967
"""
7068
{
@@ -83,17 +81,28 @@ Feature: Adding new user
8381
"type": "string"
8482
}
8583
"""
86-
# here is used qjson "json-path" syntax to find JSON node
84+
# here are used two different json-path engines (gjson & oliveagle) to find JSON nodes
8785
And the "JSON" node "firstName" should be "string" of value "{{.RANDOM_FIRST_NAME}}"
88-
# here is used oliveagle "json-path" syntax to find JSON node
8986
And the "JSON" node "$.lastName" should be "string" of value "doe-{{.RANDOM_LAST_NAME}}"
87+
88+
# here we look for substrings
89+
And the "JSON" node "$.lastName" should not contain sub string "smith"
90+
But the "JSON" node "$.lastName" should contain sub string "doe"
91+
9092
# here is used regExp acceptable by standard go package "regExp"
9193
And the "JSON" node "lastName" should not match regExp "smith-.*"
9294
But the "JSON" node "lastName" should match regExp "doe-.*"
95+
96+
# assertion may be based on one of JSON data types: array, boolean, null, number, object
9397
And the "JSON" node "age" should not be "string"
98+
But the "JSON" node "$.age" should be "number"
99+
And the "JSON" node "$.age" should be "number" and contain one of values "18, 19, 20"
100+
101+
# or on one of Go-like data types: bool, float, int, map, slice, string
94102
But the "JSON" node "$.age" should be "int"
95103
And the "JSON" node "age" should be "int" of value "{{.RANDOM_AGE}}"
96104
And the "JSON" node "description" should be "string" of value "{{.RANDOM_DESCRIPTION}}"
105+
97106
# here date is formatted according to one of available formats from standard go package "time"
98107
And the "JSON" node "friendSince" should be "string" of value "{{.MEET_DATE.Format `2006-01-02T15:04:05Z`}}"
99108

features/test_server/json/get_many.feature

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ Feature: Fetching many users.
8383
And the response should have header "Content-Type" of value "application/json; charset=UTF-8"
8484
And the response body should have format "JSON"
8585
# here we only check only node type, not its exact value
86-
And the "JSON" node "root" should be "slice"
87-
But the "JSON" node "root" should not be slice of length "0"
88-
And the "JSON" node "root" should not be "nil"
86+
And the "JSON" node "@this" should be "slice"
87+
But the "JSON" node "@this" should not be slice of length "0"
88+
And the "JSON" node "@this" should not be "nil"
89+
And the "JSON" node "@this" should not be "null"

features/test_server/json/get_one.feature

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ Feature: Fetching single user.
5555
And the response body should be valid according to schema "user/user.json"
5656
And the "JSON" node "firstName" should be "string" of value "{{.RANDOM_FIRST_NAME}}"
5757
And the "JSON" node "lastName" should be "string" of value "{{.RANDOM_LAST_NAME}}"
58-
And the "JSON" node "age" should be "int" of value "{{.RANDOM_AGE}}"
59-
And the "JSON" node "id" should be "int" of value "{{.USER_ID}}"
58+
And the "JSON" node "age" should be "number" of value "{{.RANDOM_AGE}}"
59+
And the "JSON" node "id" should be "number" of value "{{.USER_ID}}"
6060
And the "JSON" node "description" should be "string" of value "{{.RANDOM_DESCRIPTION}}"
6161
And the "JSON" node "friendSince" should be "string" of value "{{.MEET_DATE.Format `2006-01-02T15:04:05Z`}}"
6262

features/test_server/json/replace.feature

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ Feature: Replacing single user account.
5555
And the response body should be valid according to schema "user/user.json"
5656
And the "JSON" node "firstName" should be "string" of value "{{.RANDOM_FIRST_NAME}}"
5757
And the "JSON" node "lastName" should be "string" of value "{{.RANDOM_LAST_NAME}}"
58-
And the "JSON" node "age" should be "int" of value "{{.RANDOM_AGE}}"
59-
And the "JSON" node "id" should be "int" of value "{{.USER_ID}}"
58+
And the "JSON" node "age" should be "number" of value "{{.RANDOM_AGE}}"
59+
And the "JSON" node "id" should be "number" of value "{{.USER_ID}}"
6060
And the "JSON" node "description" should be "string" of value "{{.RANDOM_DESCRIPTION}}"
6161
And the "JSON" node "friendSince" should be "string" of value "{{.MEET_DATE.Format `2006-01-02T15:04:05Z`}}"
6262

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

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

features/test_server/xml/create.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ Feature: Adding new user
5050
And the "XML" node "//firstName" should be "string" of value "{{.RANDOM_FIRST_NAME}}"
5151
And the "XML" node "//lastName" should be "string" of value "doe-{{.RANDOM_LAST_NAME}}"
5252
And the "XML" node "//lastName" should match regExp "doe-.*"
53-
And the "XML" node "//age" should be "int" of value "{{.RANDOM_AGE}}"
53+
And the "XML" node "//age" should be "integer" of value "{{.RANDOM_AGE}}"
5454
And the "XML" node "//description" should be "string" of value "{{.RANDOM_DESCRIPTION}}"
5555
And the "XML" node "//friendSince" should be "string" of value "{{.MEET_DATE.Format `2006-01-02T15:04:05Z`}}"
5656

0 commit comments

Comments
 (0)