Skip to content

Commit

Permalink
Merge pull request #180 from ikramanop/feature/cases-variables-throug…
Browse files Browse the repository at this point in the history
…h-tests

feature: add cases variables
  • Loading branch information
fetinin authored Jan 9, 2023
2 parents 30145e8 + 0b4f879 commit 95be036
Show file tree
Hide file tree
Showing 13 changed files with 277 additions and 23 deletions.
20 changes: 20 additions & 0 deletions README-ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,26 @@ password=private_password

env-файл, например, удобно использовать, когда нужно вынести из теста приватную информацию (пароли, ключи и т.п.)

#### В cases

Переменные могут быть заданы в блоке *cases*.

Пример:

```yaml
- name: Get user info
method: GET
path: "/user/1"
response:
200: '{ "user_id": "1", "name": "{{ $name }}", "surname": "{{ $surname }}" }'
cases:
- variables:
name: John
surname: Doe
```

Такие переменные будут доступны и в других кейсах, если не будут переопределены.

## Загрузка файлов

В тестовом запросе можно загружать файлы. Для этого нужно указать тип запроса - POST и заголовок:
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,26 @@ password=private_password

env-file can be convenient to hide sensitive information from a test (passwords, keys, etc.)

#### From cases

You can describe variables in *cases* section of a test.

Example:

```yaml
- name: Get user info
method: GET
path: "/user/1"
response:
200: '{ "user_id": "1", "name": "{{ $name }}", "surname": "{{ $surname }}" }'
cases:
- variables:
name: John
surname: Doe
```

Variables like these will be available through another cases if not redefined.

## Files uploading

You can upload files in test request. For this you must specify the type of request - POST and header:
Expand Down
25 changes: 25 additions & 0 deletions examples/with-cases-example/cases/do.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
- name: Test /do endpoint
method: POST
path: /do

variables:
name: name

request: '{"{{ $name }}": "{{ .name }}"}'

response:
200: '{"{{ $num }}":{{ .num }}}'

cases:
- variables:
num: num
requestArgs:
name: a
responseArgs:
200:
num: 1
- requestArgs:
name: b
responseArgs:
200:
num: 2
28 changes: 28 additions & 0 deletions examples/with-cases-example/func_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package main

import (
"net/http/httptest"
"os"
"testing"

"github.com/lamoda/gonkey/mocks"
"github.com/lamoda/gonkey/runner"
)

func TestProxy(t *testing.T) {
m := mocks.NewNop("backend")
if err := m.Start(); err != nil {
t.Fatal(err)
}
defer m.Shutdown()

os.Setenv("BACKEND_ADDR", m.Service("backend").ServerAddr())
initServer()
srv := httptest.NewServer(nil)

runner.RunWithTesting(t, &runner.RunWithTestingParams{
Server: srv,
TestsDir: "cases",
Mocks: m,
})
}
62 changes: 62 additions & 0 deletions examples/with-cases-example/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)

type Request struct {
Name string `json:"name"`
}

type Response struct {
Num int `json:"num"`
}

func main() {
initServer()
log.Fatal(http.ListenAndServe(":8080", nil))
}

func initServer() {
http.HandleFunc("/do", Do)
}

func Do(w http.ResponseWriter, r *http.Request) {
var response Response

if r.Method != "POST" {
response.Num = 0
fmt.Fprint(w, buildResponse(response))
return
}

jsonRequest, _ := ioutil.ReadAll(r.Body)
request := buildRequest(jsonRequest)

if request.Name == "a" {
response.Num = 1
fmt.Fprint(w, buildResponse(response))
return
}

response.Num = 2
fmt.Fprint(w, buildResponse(response))
}

func buildRequest(jsonRequest []byte) Request {
var request Request

json.Unmarshal(jsonRequest, &request)

return request
}

func buildResponse(response Response) string {
jsonResponse, _ := json.Marshal(response)

return string(jsonResponse)
}
1 change: 1 addition & 0 deletions models/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type TestInterface interface {
DbQueryString() string
DbResponseJson() []string
GetVariables() map[string]string
GetCombinedVariables() map[string]string
GetVariablesToSet() map[int]map[string]string
GetDatabaseChecks() []DatabaseCheck
SetDatabaseChecks([]DatabaseCheck)
Expand Down
4 changes: 2 additions & 2 deletions runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func (r *Runner) executeTest(v models.TestInterface) (*models.Result, error) {
}
}

r.config.Variables.Load(v.GetVariables())
r.config.Variables.Load(v.GetCombinedVariables())
v = r.config.Variables.Apply(v)

// load fixtures
Expand Down Expand Up @@ -206,7 +206,7 @@ func (r *Runner) executeTest(v models.TestInterface) (*models.Result, error) {
return nil, err
}

r.config.Variables.Load(v.GetVariables())
r.config.Variables.Load(v.GetCombinedVariables())
v = r.config.Variables.Apply(v)

for _, c := range r.checkers {
Expand Down
27 changes: 26 additions & 1 deletion testloader/yaml_file/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,22 @@ import (
"bytes"
"fmt"
"io/ioutil"
"regexp"
"strings"
"text/template"

"github.com/lamoda/gonkey/models"

"gopkg.in/yaml.v2"
)

const (
gonkeyVariableLeftPart = "{{ $"
gonkeyProtectSubstitute = "!protect!"
)

var gonkeyProtectTemplate = regexp.MustCompile("{{\\s*\\$")

func parseTestDefinitionFile(absPath string) ([]Test, error) {
data, err := ioutil.ReadFile(absPath)
if err != nil {
Expand Down Expand Up @@ -37,6 +47,8 @@ func parseTestDefinitionFile(absPath string) ([]Test, error) {
}

func substituteArgs(tmpl string, args map[string]interface{}) (string, error) {
tmpl = gonkeyProtectTemplate.ReplaceAllString(tmpl, gonkeyProtectSubstitute)

compiledTmpl, err := template.New("").Parse(tmpl)
if err != nil {
return "", err
Expand All @@ -48,7 +60,9 @@ func substituteArgs(tmpl string, args map[string]interface{}) (string, error) {
return "", err
}

return buf.String(), nil
tmpl = strings.ReplaceAll(buf.String(), gonkeyProtectSubstitute, gonkeyVariableLeftPart)

return tmpl, nil
}

func substituteArgsToMap(tmpl map[string]string, args map[string]interface{}) (map[string]string, error) {
Expand Down Expand Up @@ -77,6 +91,7 @@ func makeTestFromDefinition(filePath string, testDefinition TestDefinition) ([]T
test.AfterRequestScript = testDefinition.AfterRequestScriptParams.PathTmpl
test.DbQuery = testDefinition.DbQueryTmpl
test.DbResponse = testDefinition.DbResponseTmpl
test.CombinedVariables = testDefinition.Variables

dbChecks := []models.DatabaseCheck{}
for _, check := range testDefinition.DatabaseChecks {
Expand All @@ -97,6 +112,11 @@ func makeTestFromDefinition(filePath string, testDefinition TestDefinition) ([]T
headersValTmpl := testDefinition.HeadersVal
cookiesValTmpl := testDefinition.CookiesVal
responseHeadersTmpl := testDefinition.ResponseHeaders
combinedVariables := map[string]string{}

if testDefinition.Variables != nil {
combinedVariables = testDefinition.Variables
}

// produce as many tests as cases defined
for caseIdx, testCase := range testDefinition.Cases {
Expand Down Expand Up @@ -175,6 +195,11 @@ func makeTestFromDefinition(filePath string, testDefinition TestDefinition) ([]T
return nil, err
}

for key, value := range testCase.Variables {
combinedVariables[key] = value.(string)
}
test.CombinedVariables = combinedVariables

// compile DbResponse
if testCase.DbResponse != nil {
// DbResponse from test case has top priority
Expand Down
2 changes: 2 additions & 0 deletions testloader/yaml_file/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ var testsYAMLData = `
200:
foo: 'Hello world'
bar: 42
variables:
newVar: some_value
`

func TestParseTestsWithCases(t *testing.T) {
Expand Down
6 changes: 6 additions & 0 deletions testloader/yaml_file/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ type Test struct {
DbQuery string
DbResponse []string

CombinedVariables map[string]string

DbChecks []models.DatabaseCheck
}

Expand Down Expand Up @@ -147,6 +149,10 @@ func (t *Test) GetVariables() map[string]string {
return t.Variables
}

func (t *Test) GetCombinedVariables() map[string]string {
return t.CombinedVariables
}

func (t *Test) GetForm() *models.Form {
return t.Form
}
Expand Down
33 changes: 17 additions & 16 deletions testloader/yaml_file/test_definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type CaseData struct {
DbQueryArgs map[string]interface{} `json:"dbQueryArgs" yaml:"dbQueryArgs"`
DbResponseArgs map[string]interface{} `json:"dbResponseArgs" yaml:"dbResponseArgs"`
DbResponse []string `json:"dbResponse" yaml:"dbResponse"`
Variables map[string]interface{} `json:"variables" yaml:"variables"`
}

type DatabaseCheck struct {
Expand All @@ -55,22 +56,22 @@ type VariablesToSet map[int]map[string]string

/*
There can be two types of data in yaml-file:
1) JSON-paths:
VariablesToSet:
<code1>:
<varName1>: <JSON_Path1>
<varName2>: <JSON_Path2>
2) Plain text:
VariablesToSet:
<code1>: <varName1>
<code2>: <varName2>
...
In this case we unmarshall values to format similar to JSON-paths format with empty paths:
VariablesToSet:
<code1>:
<varName1>: ""
<code2>:
<varName2>: ""
1. JSON-paths:
VariablesToSet:
<code1>:
<varName1>: <JSON_Path1>
<varName2>: <JSON_Path2>
2. Plain text:
VariablesToSet:
<code1>: <varName1>
<code2>: <varName2>
...
In this case we unmarshall values to format similar to JSON-paths format with empty paths:
VariablesToSet:
<code1>:
<varName1>: ""
<code2>:
<varName2>: ""
*/
func (v *VariablesToSet) UnmarshalYAML(unmarshal func(interface{}) error) error {

Expand Down
Loading

0 comments on commit 95be036

Please sign in to comment.