From e9919b6285bd0b3fcea0d223d771135bdd0b5d8c Mon Sep 17 00:00:00 2001 From: Stanislav Fedii Date: Tue, 26 Nov 2019 23:41:20 -0500 Subject: [PATCH 01/10] Started implementation of before and after features. --- after.go | 7 +++++++ before.go | 26 ++++++++++++++++++++++++++ result.go | 15 --------------- 3 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 after.go create mode 100644 before.go diff --git a/after.go b/after.go new file mode 100644 index 0000000..85e9b68 --- /dev/null +++ b/after.go @@ -0,0 +1,7 @@ +package allure + +import "testing" + +func After(t *testing.T, description string, action func()) { + +} diff --git a/before.go b/before.go new file mode 100644 index 0000000..3e67414 --- /dev/null +++ b/before.go @@ -0,0 +1,26 @@ +package allure + +type Container struct { + Uuid string `json:"uuid"` + Name string `json:"name"` + Children []string `json:"children"` + Description string `json:"description"` + Befores []helperContainer `json:"befores"` + Afters []helperContainer `json:"afters"` + Links []string `json:"links"` + Start int64 `json:"start"` + Stop int64 `json:"stop"` +} + +//helperContainer defines a step +type helperContainer struct { + Name string `json:"name,omitempty"` + Status string `json:"status,omitempty"` + StatusDetails *statusDetails `json:"statusDetails,omitempty"` + Stage string `json:"stage,omitempty"` + Description string `json:"description,omitempty"` + Start int64 `json:"start,omitempty"` + Stop int64 `json:"stop,omitempty"` + Steps []stepObject `json:"steps,omitempty"` + Attachments []attachment `json:"attachments,omitempty"` +} diff --git a/result.go b/result.go index 2ee74af..0260392 100644 --- a/result.go +++ b/result.go @@ -28,26 +28,11 @@ type result struct { Parameters []Parameter `json:"parameters,omitempty"` Start int64 `json:"start,omitempty"` Stop int64 `json:"stop,omitempty"` - Children []string `json:"children,omitempty"` - Befores []Before `json:"befores,omitempty"` FullName string `json:"fullName,omitempty"` Labels []Label `json:"labels,omitempty"` } type FailureMode string -//Before defines a step -type Before struct { - Name string `json:"name,omitempty"` - Status string `json:"status,omitempty"` - StatusDetails *statusDetails `json:"statusDetails,omitempty"` - Stage string `json:"stage,omitempty"` - Description string `json:"description,omitempty"` - Start int64 `json:"start,omitempty"` - Stop int64 `json:"stop,omitempty"` - Steps []stepObject `json:"steps,omitempty"` - Attachments []attachment `json:"attachments,omitempty"` -} - // This interface provides functions required to manipulate children step records, used in the result object and // step object for recursive handling type hasSteps interface { From 1b4babed43c856cfd3717465ee9e3cfe7d3a969b Mon Sep 17 00:00:00 2001 From: Stanislav Fedii Date: Mon, 2 Dec 2019 12:26:34 -0500 Subject: [PATCH 02/10] More work on implementing before and after features. --- after.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- before.go | 16 +++++++++++++++- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/after.go b/after.go index 85e9b68..e2a578a 100644 --- a/after.go +++ b/after.go @@ -1,7 +1,58 @@ package allure -import "testing" +import ( + "fmt" + "github.com/jtolds/gls" + "log" + "os" + "testing" +) -func After(t *testing.T, description string, action func()) { +// TestWithParameters executes a test and adds parameters to the Allure result object +func AfterWithParameters(t *testing.T, description string, parameters map[string]interface{}, labels TestLabels, testFunc func()) { + after := newAfter() + afterHelper := newAfterHelper() + after.UUID = generateUUID() + afterHelper.Start = getTimestampMs() + after.Name = t.Name() + + after.Description = description + + afterHelper.Steps = make([]stepObject, 0) + if parameters == nil || len(parameters) > 0 { + afterHelper.Parameters = convertMapToParameters(parameters) + } + + defer func() { + afterHelper.Stop = getTimestampMs() + afterHelper.Status = getTestStatus(t) + afterHelper.Stage = "finished" + + err := after.writeResultsFile() + if err != nil { + log.Fatalf(fmt.Sprintf("Failed to write content of result to json file"), err) + os.Exit(1) + } + }() + ctxMgr.SetValues(gls.Values{ + testResultKey: afterHelper, + nodeKey: afterHelper, + testInstanceKey: t, + }, testFunc) +} + +//Test execute the test and creates an Allure result used by Allure reports +func After(t *testing.T, description string, testFunc func()) { + TestWithParameters(t, description, nil, TestLabels{}, testFunc) +} + +func newAfter() *Container { + return &Container{ + Afters: []helperContainer{*(newAfterHelper())}, + } +} + +func newAfterHelper() *helperContainer { + return newHelper() } diff --git a/before.go b/before.go index 3e67414..343d230 100644 --- a/before.go +++ b/before.go @@ -1,7 +1,7 @@ package allure type Container struct { - Uuid string `json:"uuid"` + UUID string `json:"uuid"` Name string `json:"name"` Children []string `json:"children"` Description string `json:"description"` @@ -12,6 +12,11 @@ type Container struct { Stop int64 `json:"stop"` } +func (container Container) writeResultsFile() error { + //TODO: implement that + return nil +} + //helperContainer defines a step type helperContainer struct { Name string `json:"name,omitempty"` @@ -23,4 +28,13 @@ type helperContainer struct { Stop int64 `json:"stop,omitempty"` Steps []stepObject `json:"steps,omitempty"` Attachments []attachment `json:"attachments,omitempty"` + Parameters []Parameter `json:"parameters,omitempty"` +} + +func newHelper() *helperContainer { + return &helperContainer{ + Steps: make([]stepObject, 0), + Attachments: make([]attachment, 0), + Parameters: make([]Parameter, 0), + } } From 6755c97da23af4827d37782262ccbfa5588fc91d Mon Sep 17 00:00:00 2001 From: Stanislav Fedii Date: Tue, 3 Dec 2019 17:59:33 -0500 Subject: [PATCH 03/10] Implemented a draft version of setup and teardown. Some refactoring is in order. Positive scenario is successful. --- after.go | 49 ++++++------- before.go | 74 +++++++++++-------- example/example_before_after_test.go | 20 ++++++ init.go | 2 + test.go | 14 ++-- test_phase_container.go | 103 +++++++++++++++++++++++++++ 6 files changed, 203 insertions(+), 59 deletions(-) create mode 100644 example/example_before_after_test.go create mode 100644 test_phase_container.go diff --git a/after.go b/after.go index e2a578a..03947a2 100644 --- a/after.go +++ b/after.go @@ -1,58 +1,59 @@ package allure import ( - "fmt" "github.com/jtolds/gls" "log" - "os" "testing" ) -// TestWithParameters executes a test and adds parameters to the Allure result object +// AfterWithParameters executes a teardown phase of the test and adds parameters to the Allure container object func AfterWithParameters(t *testing.T, description string, parameters map[string]interface{}, labels TestLabels, testFunc func()) { after := newAfter() - afterHelper := newAfterHelper() - + afterSubContainer := after.Afters[0] after.UUID = generateUUID() - afterHelper.Start = getTimestampMs() + afterSubContainer.Start = getTimestampMs() after.Name = t.Name() after.Description = description - afterHelper.Steps = make([]stepObject, 0) + afterSubContainer.Steps = make([]stepObject, 0) if parameters == nil || len(parameters) > 0 { - afterHelper.Parameters = convertMapToParameters(parameters) + afterSubContainer.Parameters = convertMapToParameters(parameters) } defer func() { - afterHelper.Stop = getTimestampMs() - afterHelper.Status = getTestStatus(t) - afterHelper.Stage = "finished" + afterSubContainer.Stop = getTimestampMs() + afterSubContainer.Status = getTestStatus(t) + afterSubContainer.Stage = "finished" + testPhaseObject := getCurrentTestPhaseObject(t) + if testPhaseObject.Test.UUID == "" { + log.Printf("Test's \"%s\" allure teardwon is being executed before allure test!\n", t.Name()) + } + + after.Children = append(after.Children, testPhaseObject.Test.UUID) + testPhaseObject.Afters = append(testPhaseObject.Afters, after) err := after.writeResultsFile() if err != nil { - log.Fatalf(fmt.Sprintf("Failed to write content of result to json file"), err) - os.Exit(1) + log.Println("Failed to write content of result to json file", err) } }() ctxMgr.SetValues(gls.Values{ - testResultKey: afterHelper, - nodeKey: afterHelper, + testResultKey: afterSubContainer, + nodeKey: afterSubContainer, testInstanceKey: t, }, testFunc) } -//Test execute the test and creates an Allure result used by Allure reports +//After executes the teardown phase of the test and creates an Allure container object used by Allure reports func After(t *testing.T, description string, testFunc func()) { - TestWithParameters(t, description, nil, TestLabels{}, testFunc) + AfterWithParameters(t, description, nil, TestLabels{}, testFunc) } -func newAfter() *Container { - return &Container{ - Afters: []helperContainer{*(newAfterHelper())}, +func newAfter() *container { + return &container{ + Children: make([]string, 0), + Links: make([]string, 0), + Afters: []*subContainer{newHelper()}, } } - -func newAfterHelper() *helperContainer { - return newHelper() -} diff --git a/before.go b/before.go index 343d230..8092908 100644 --- a/before.go +++ b/before.go @@ -1,40 +1,52 @@ package allure -type Container struct { - UUID string `json:"uuid"` - Name string `json:"name"` - Children []string `json:"children"` - Description string `json:"description"` - Befores []helperContainer `json:"befores"` - Afters []helperContainer `json:"afters"` - Links []string `json:"links"` - Start int64 `json:"start"` - Stop int64 `json:"stop"` -} +import ( + "github.com/jtolds/gls" + "log" + "testing" +) + +// BeforeWithParameters executes a setup phase of the test and adds parameters to the Allure container object +func BeforeWithParameters(t *testing.T, description string, parameters map[string]interface{}, labels TestLabels, testFunc func()) { + testPhaseObject := getCurrentTestPhaseObject(t) + if testPhaseObject.Test != nil { + log.Printf("Test's \"%s\" allure setup is being executed after allure test!\n", t.Name()) + } + before := newBefore() + testPhaseObject.Befores = append(testPhaseObject.Befores, before) + beforeSubContainer := before.Befores[0] + + before.UUID = generateUUID() + beforeSubContainer.Start = getTimestampMs() + before.Name = t.Name() + + before.Description = description + + beforeSubContainer.Steps = make([]stepObject, 0) + if parameters == nil || len(parameters) > 0 { + beforeSubContainer.Parameters = convertMapToParameters(parameters) + } + + defer func() { + beforeSubContainer.Stop = getTimestampMs() + beforeSubContainer.Status = getTestStatus(t) + beforeSubContainer.Stage = "finished" -func (container Container) writeResultsFile() error { - //TODO: implement that - return nil + }() + ctxMgr.SetValues(gls.Values{ + testResultKey: beforeSubContainer, + nodeKey: beforeSubContainer, + testInstanceKey: t, + }, testFunc) } -//helperContainer defines a step -type helperContainer struct { - Name string `json:"name,omitempty"` - Status string `json:"status,omitempty"` - StatusDetails *statusDetails `json:"statusDetails,omitempty"` - Stage string `json:"stage,omitempty"` - Description string `json:"description,omitempty"` - Start int64 `json:"start,omitempty"` - Stop int64 `json:"stop,omitempty"` - Steps []stepObject `json:"steps,omitempty"` - Attachments []attachment `json:"attachments,omitempty"` - Parameters []Parameter `json:"parameters,omitempty"` +//Before executes the setup phase of the test and creates an Allure container object used by Allure reports +func Before(t *testing.T, description string, testFunc func()) { + BeforeWithParameters(t, description, nil, TestLabels{}, testFunc) } -func newHelper() *helperContainer { - return &helperContainer{ - Steps: make([]stepObject, 0), - Attachments: make([]attachment, 0), - Parameters: make([]Parameter, 0), +func newBefore() *container { + return &container{ + Befores: []*subContainer{newHelper()}, } } diff --git a/example/example_before_after_test.go b/example/example_before_after_test.go new file mode 100644 index 0000000..8be7988 --- /dev/null +++ b/example/example_before_after_test.go @@ -0,0 +1,20 @@ +package example + +import ( + "github.com/dailymotion/allure-go" + "testing" +) + +func TestAllureSetupTeardown(t *testing.T) { + allure.Before(t, "setup", func() { + allure.Step("Setup step 1", func() {}) + }) + + allure.Test(t, "actual test", func() { + allure.Step("Test step 1", func() {}) + }) + + allure.After(t, "teardown", func() { + allure.Step("teardown step 1", func() {}) + }) +} diff --git a/init.go b/init.go index 6549783..7b57377 100644 --- a/init.go +++ b/init.go @@ -11,6 +11,7 @@ var ( resultsPath string createFolderOnce sync.Once copyEnvFileOnce sync.Once + testPhaseObjects map[string]*testPhaseContainer ) const ( @@ -24,4 +25,5 @@ const ( func init() { ctxMgr = gls.NewContextManager() + testPhaseObjects = make(map[string]*testPhaseContainer) } diff --git a/test.go b/test.go index e20f13e..234c412 100644 --- a/test.go +++ b/test.go @@ -1,12 +1,10 @@ package allure import ( - "fmt" "github.com/dailymotion/allure-go/severity" "github.com/fatih/camelcase" "github.com/jtolds/gls" "log" - "os" "strings" "testing" ) @@ -43,14 +41,22 @@ func TestWithParameters(t *testing.T, description string, parameters map[string] } defer func() { + getCurrentTestPhaseObject(t).Test = r r.Stop = getTimestampMs() r.Status = getTestStatus(t) r.Stage = "finished" err := r.writeResultsFile() if err != nil { - log.Fatalf(fmt.Sprintf("Failed to write content of result to json file"), err) - os.Exit(1) + log.Println("Failed to write content of result to json file", err) + } + setups := getCurrentTestPhaseObject(t).Befores + for _, setup := range setups { + setup.Children = append(setup.Children, r.UUID) + err := setup.writeResultsFile() + if err != nil { + log.Println("Failed to write content of result to json file", err) + } } }() ctxMgr.SetValues(gls.Values{ diff --git a/test_phase_container.go b/test_phase_container.go new file mode 100644 index 0000000..1890d8b --- /dev/null +++ b/test_phase_container.go @@ -0,0 +1,103 @@ +package allure + +import ( + "encoding/json" + "fmt" + "github.com/pkg/errors" + "io/ioutil" + "testing" +) + +type testPhaseContainer struct { + Befores []*container + Test *result + Afters []*container +} + +type container struct { + UUID string `json:"uuid"` + Name string `json:"name"` + Children []string `json:"children"` + Description string `json:"description"` + Befores []*subContainer `json:"befores"` + Afters []*subContainer `json:"afters"` + Links []string `json:"links"` + Start int64 `json:"start"` + Stop int64 `json:"stop"` +} + +//subContainer defines a step +type subContainer struct { + Name string `json:"name,omitempty"` + Status string `json:"status,omitempty"` + StatusDetails *statusDetails `json:"statusDetails,omitempty"` + Stage string `json:"stage,omitempty"` + Description string `json:"description,omitempty"` + Start int64 `json:"start,omitempty"` + Stop int64 `json:"stop,omitempty"` + Steps []stepObject `json:"steps,omitempty"` + Attachments []attachment `json:"attachments,omitempty"` + Parameters []Parameter `json:"parameters,omitempty"` +} + +func (sc *subContainer) GetAttachments() []attachment { + return sc.Attachments +} + +func (sc *subContainer) AddAttachment(attachment attachment) { + sc.Attachments = append(sc.Attachments, attachment) +} + +func (sc *subContainer) GetSteps() []stepObject { + return sc.Steps +} + +func (sc *subContainer) AddStep(step stepObject) { + sc.Steps = append(sc.Steps, step) +} + +func getCurrentTestPhaseObject(t *testing.T) *testPhaseContainer { + var currentPhaseObject *testPhaseContainer + if phaseContainer, ok := testPhaseObjects[t.Name()]; ok { + currentPhaseObject = phaseContainer + } else { + currentPhaseObject = &testPhaseContainer{ + Befores: make([]*container, 0), + Afters: make([]*container, 0), + } + testPhaseObjects[t.Name()] = currentPhaseObject + } + + return currentPhaseObject +} + +//func getCurrentTestUUID(t *testing.T) string { +// if currentTest := getCurrentTestPhaseObject(t).Test; currentTest != nil { +// return currentTest.UUID +// } else { +// return "" +// } +//} + +func (c container) writeResultsFile() error { + createFolderOnce.Do(createFolderIfNotExists) + copyEnvFileOnce.Do(copyEnvFileIfExists) + + j, err := json.Marshal(c) + if err != nil { + return errors.Wrap(err, "Failed to marshall result into JSON") + } + err = ioutil.WriteFile(fmt.Sprintf("%s/%s-container.json", resultsPath, c.UUID), j, 0777) + if err != nil { + return errors.Wrap(err, "Failed to write in file") + } + return nil +} + +func newHelper() *subContainer { + return &subContainer{ + Steps: make([]stepObject, 0), + Attachments: make([]attachment, 0), + Parameters: make([]Parameter, 0), + } +} From e115532f1e1a750cb271c614f20426ba05d9959f Mon Sep 17 00:00:00 2001 From: Stanislav Fedii Date: Thu, 5 Dec 2019 22:28:03 -0500 Subject: [PATCH 04/10] Renamed Before and After functions to distinguish per-test and per-folder (per-suite functions). --- after.go | 10 +++++----- before.go | 10 +++++----- example/example_before_after_test.go | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/after.go b/after.go index 03947a2..7dd85e6 100644 --- a/after.go +++ b/after.go @@ -6,8 +6,8 @@ import ( "testing" ) -// AfterWithParameters executes a teardown phase of the test and adds parameters to the Allure container object -func AfterWithParameters(t *testing.T, description string, parameters map[string]interface{}, labels TestLabels, testFunc func()) { +// AfterTestWithParameters executes a teardown phase of the test and adds parameters to the Allure container object +func AfterTestWithParameters(t *testing.T, description string, parameters map[string]interface{}, labels TestLabels, testFunc func()) { after := newAfter() afterSubContainer := after.Afters[0] after.UUID = generateUUID() @@ -45,9 +45,9 @@ func AfterWithParameters(t *testing.T, description string, parameters map[string }, testFunc) } -//After executes the teardown phase of the test and creates an Allure container object used by Allure reports -func After(t *testing.T, description string, testFunc func()) { - AfterWithParameters(t, description, nil, TestLabels{}, testFunc) +//AfterTest executes the teardown phase of the test and creates an Allure container object used by Allure reports +func AfterTest(t *testing.T, description string, testFunc func()) { + AfterTestWithParameters(t, description, nil, TestLabels{}, testFunc) } func newAfter() *container { diff --git a/before.go b/before.go index 8092908..4fb1a59 100644 --- a/before.go +++ b/before.go @@ -6,8 +6,8 @@ import ( "testing" ) -// BeforeWithParameters executes a setup phase of the test and adds parameters to the Allure container object -func BeforeWithParameters(t *testing.T, description string, parameters map[string]interface{}, labels TestLabels, testFunc func()) { +// BeforeTestWithParameters executes a setup phase of the test and adds parameters to the Allure container object +func BeforeTestWithParameters(t *testing.T, description string, parameters map[string]interface{}, labels TestLabels, testFunc func()) { testPhaseObject := getCurrentTestPhaseObject(t) if testPhaseObject.Test != nil { log.Printf("Test's \"%s\" allure setup is being executed after allure test!\n", t.Name()) @@ -40,9 +40,9 @@ func BeforeWithParameters(t *testing.T, description string, parameters map[strin }, testFunc) } -//Before executes the setup phase of the test and creates an Allure container object used by Allure reports -func Before(t *testing.T, description string, testFunc func()) { - BeforeWithParameters(t, description, nil, TestLabels{}, testFunc) +//BeforeTest executes the setup phase of the test and creates an Allure container object used by Allure reports +func BeforeTest(t *testing.T, description string, testFunc func()) { + BeforeTestWithParameters(t, description, nil, TestLabels{}, testFunc) } func newBefore() *container { diff --git a/example/example_before_after_test.go b/example/example_before_after_test.go index 8be7988..d177c8d 100644 --- a/example/example_before_after_test.go +++ b/example/example_before_after_test.go @@ -6,7 +6,7 @@ import ( ) func TestAllureSetupTeardown(t *testing.T) { - allure.Before(t, "setup", func() { + allure.BeforeTest(t, "setup", func() { allure.Step("Setup step 1", func() {}) }) @@ -14,7 +14,7 @@ func TestAllureSetupTeardown(t *testing.T) { allure.Step("Test step 1", func() {}) }) - allure.After(t, "teardown", func() { + allure.AfterTest(t, "teardown", func() { allure.Step("teardown step 1", func() {}) }) } From 9b1018e41423ae9bd3d8c619af7cd66260879fe8 Mon Sep 17 00:00:00 2001 From: Stanislav Fedii Date: Fri, 13 Dec 2019 17:24:05 -0500 Subject: [PATCH 05/10] Implemented handling of error in setup and updated README.md. --- README.md | 14 +++++------ after.go | 2 +- attachment.go | 2 +- error.go | 8 +++--- example/example_before_after_test.go | 9 +++++++ hasattachments.go | 6 +++++ hasstatus.go | 6 +++++ hasstatusdetails.go | 6 +++++ hassteps.go | 8 ++++++ result.go | 37 +++++++++++----------------- step.go | 16 ++++++------ test.go | 19 ++++++++++---- test_phase_container.go | 24 +++++++++++++++--- 13 files changed, 104 insertions(+), 53 deletions(-) create mode 100644 hasattachments.go create mode 100644 hasstatus.go create mode 100644 hasstatusdetails.go create mode 100644 hassteps.go diff --git a/README.md b/README.md index 356b110..d494a52 100644 --- a/README.md +++ b/README.md @@ -76,13 +76,13 @@ This project is still in progress. Here is the list of available features : Here is the list of improvements that are needed for existing features : - [X] Manage failure at step level : the project must be able to mark a Step as failed (red in the report) when an assertion fails. The assertion stacktrace must appear in the report. -- [ ] Manage error at test and step level : the project must be able to mark a Test and Step as broken (orange in the report) when an error happens. The error stacktrace must appear in the report. -- [ ] Add support of Links +- [X] Manage error at test and step level : the project must be able to mark a Test and Step as broken (orange in the report) when an error happens. The error stacktrace must appear in the report. +- [X] Add support of Links - [ ] Add support of Unknown status for Test object -- [ ] Add support of Set up in Execution part of a Test -- [ ] Add support of Tear down in Execution part of a Test -- [ ] Add support of Severity +- [X] Add support of Set up in Execution part of a Test +- [X] Add support of Tear down in Execution part of a Test +- [X] Add support of Severity - [ ] Add support of Flaky tag -- [ ] Add support of Categories -- [ ] Add support of Features +- [X] Add support of Categories +- [X] Add support of Features - [X] Add support for environment files \ No newline at end of file diff --git a/after.go b/after.go index 7dd85e6..547f46d 100644 --- a/after.go +++ b/after.go @@ -27,7 +27,7 @@ func AfterTestWithParameters(t *testing.T, description string, parameters map[st afterSubContainer.Stage = "finished" testPhaseObject := getCurrentTestPhaseObject(t) if testPhaseObject.Test.UUID == "" { - log.Printf("Test's \"%s\" allure teardwon is being executed before allure test!\n", t.Name()) + log.Printf("Test's \"%s\" allure teardщwn is being executed before allure test!\n", t.Name()) } after.Children = append(after.Children, testPhaseObject.Test.UUID) diff --git a/attachment.go b/attachment.go index 0cb00f1..6f877d0 100644 --- a/attachment.go +++ b/attachment.go @@ -25,7 +25,7 @@ func AddAttachment(name string, mimeType MimeType, content []byte) error { return errors.Wrap(err, "Failed to create an attachment file") } if node, ok := ctxMgr.GetValue(nodeKey); ok { - node.(hasAttachments).AddAttachment(*attachment) + node.(hasAttachments).addAttachment(*attachment) } return nil diff --git a/error.go b/error.go index fb57717..dda3c31 100644 --- a/error.go +++ b/error.go @@ -35,19 +35,19 @@ func allureError(err error, status string, now bool) { manipulateOnObjectFromCtx( testResultKey, func(testResult interface{}) { - testStatusDetails := testResult.(*result).StatusDetails + testStatusDetails := testResult.(hasStatusDeatils).getStatusDetails() if testStatusDetails == nil { testStatusDetails = &statusDetails{} } testStatusDetails.Trace = filterStackTrace(debug.Stack()) testStatusDetails.Message = err.Error() - testResult.(*result).StatusDetails = testStatusDetails - testResult.(*result).Status = status + testResult.(hasStatusDeatils).setStatusDetails(*testStatusDetails) + testResult.(hasStatus).setStatus(status) }) manipulateOnObjectFromCtx( nodeKey, func(node interface{}) { - node.(hasStatus).SetStatus(status) + node.(hasStatus).setStatus(status) fmt.Printf("Set %+v status to %s\n", node, status) }) manipulateOnObjectFromCtx( diff --git a/example/example_before_after_test.go b/example/example_before_after_test.go index d177c8d..f1112d6 100644 --- a/example/example_before_after_test.go +++ b/example/example_before_after_test.go @@ -1,6 +1,7 @@ package example import ( + "errors" "github.com/dailymotion/allure-go" "testing" ) @@ -18,3 +19,11 @@ func TestAllureSetupTeardown(t *testing.T) { allure.Step("teardown step 1", func() {}) }) } + +func TestAllureSetupFailed(t *testing.T) { + allure.BeforeTest(t, "setup failed", func() { + allure.Fail(errors.New("some error")) + }) + + allure.Test(t, "actual test", func() {}) +} diff --git a/hasattachments.go b/hasattachments.go new file mode 100644 index 0000000..e4461a3 --- /dev/null +++ b/hasattachments.go @@ -0,0 +1,6 @@ +package allure + +type hasAttachments interface { + getAttachments() []attachment + addAttachment(attachment attachment) +} diff --git a/hasstatus.go b/hasstatus.go new file mode 100644 index 0000000..a3448e4 --- /dev/null +++ b/hasstatus.go @@ -0,0 +1,6 @@ +package allure + +type hasStatus interface { + setStatus(status string) + getStatus() string +} diff --git a/hasstatusdetails.go b/hasstatusdetails.go new file mode 100644 index 0000000..d6894e6 --- /dev/null +++ b/hasstatusdetails.go @@ -0,0 +1,6 @@ +package allure + +type hasStatusDeatils interface { + getStatusDetails() *statusDetails + setStatusDetails(details statusDetails) +} diff --git a/hassteps.go b/hassteps.go new file mode 100644 index 0000000..bd06707 --- /dev/null +++ b/hassteps.go @@ -0,0 +1,8 @@ +package allure + +// This interface provides functions required to manipulate children step records, used in the result object and +// step object for recursive handling +type hasSteps interface { + getSteps() []stepObject + addStep(step stepObject) +} diff --git a/result.go b/result.go index e7dc9fb..d034adc 100644 --- a/result.go +++ b/result.go @@ -33,47 +33,38 @@ type result struct { } type FailureMode string -// This interface provides functions required to manipulate children step records, used in the result object and -// step object for recursive handling -type hasSteps interface { - GetSteps() []stepObject - AddStep(step stepObject) -} - -type hasAttachments interface { - GetAttachments() []attachment - AddAttachment(attachment attachment) -} - -type hasStatus interface { - SetStatus(status string) - GetStatus() string -} - -func (r *result) GetAttachments() []attachment { +func (r *result) getAttachments() []attachment { return r.Attachments } -func (r *result) AddAttachment(attachment attachment) { +func (r *result) addAttachment(attachment attachment) { r.Attachments = append(r.Attachments, attachment) } -func (r *result) GetSteps() []stepObject { +func (r *result) getSteps() []stepObject { return r.Steps } -func (r *result) AddStep(step stepObject) { +func (r *result) addStep(step stepObject) { r.Steps = append(r.Steps, step) } -func (r *result) SetStatus(status string) { +func (r *result) setStatus(status string) { r.Status = status } -func (r *result) GetStatus() string { +func (r *result) getStatus() string { return r.Status } +func (r *result) getStatusDetails() *statusDetails { + return r.StatusDetails +} + +func (r *result) setStatusDetails(details statusDetails) { + r.StatusDetails = &details +} + func (r *result) setLabels(t *testing.T, labels TestLabels) { wsd := os.Getenv(wsPathEnvKey) diff --git a/step.go b/step.go index ebc3877..ccc32f5 100644 --- a/step.go +++ b/step.go @@ -19,27 +19,27 @@ type stepObject struct { Stop int64 `json:"stop"` } -func (s *stepObject) GetSteps() []stepObject { +func (s *stepObject) getSteps() []stepObject { return s.ChildrenSteps } -func (s *stepObject) AddStep(step stepObject) { +func (s *stepObject) addStep(step stepObject) { s.ChildrenSteps = append(s.ChildrenSteps, step) } -func (s *stepObject) GetAttachments() []attachment { +func (s *stepObject) getAttachments() []attachment { return s.Attachments } -func (s *stepObject) AddAttachment(attachment attachment) { +func (s *stepObject) addAttachment(attachment attachment) { s.Attachments = append(s.Attachments, attachment) } -func (s *stepObject) SetStatus(status string) { +func (s *stepObject) setStatus(status string) { s.Status = status } -func (s *stepObject) GetStatus() string { +func (s *stepObject) getStatus() string { return s.Status } @@ -76,7 +76,7 @@ func StepWithParameter(description string, parameters map[string]interface{}, ac }) manipulateOnObjectFromCtx(nodeKey, func(currentStepObj interface{}) { currentStep := currentStepObj.(hasSteps) - currentStep.AddStep(*step) + currentStep.addStep(*step) }) }() @@ -100,7 +100,7 @@ func SkipStepWithParameter(description, reason string, parameters map[string]int step.StatusDetail.Message = reason if currentStepObj, ok := ctxMgr.GetValue(nodeKey); ok { currentStep := currentStepObj.(hasSteps) - currentStep.AddStep(*step) + currentStep.addStep(*step) } else { log.Fatalln("could not retrieve current allure node") } diff --git a/test.go b/test.go index 7917e9a..b81ae66 100644 --- a/test.go +++ b/test.go @@ -61,11 +61,20 @@ func TestWithParameters(t *testing.T, description string, parameters map[string] } } }() - ctxMgr.SetValues(gls.Values{ - testResultKey: r, - nodeKey: r, - testInstanceKey: t, - }, testFunc) + if !t.Failed() { + ctxMgr.SetValues(gls.Values{ + testResultKey: r, + nodeKey: r, + testInstanceKey: t, + }, testFunc) + } else { + //ctxMgr.SetValues(gls.Values{ + // testResultKey: r, + // nodeKey: r, + // testInstanceKey: t, + //}, func() {}) + r.Status = skipped + } } //Test execute the test and creates an Allure result used by Allure reports diff --git a/test_phase_container.go b/test_phase_container.go index 1890d8b..df9776d 100644 --- a/test_phase_container.go +++ b/test_phase_container.go @@ -40,22 +40,38 @@ type subContainer struct { Parameters []Parameter `json:"parameters,omitempty"` } -func (sc *subContainer) GetAttachments() []attachment { +func (sc *subContainer) getAttachments() []attachment { return sc.Attachments } -func (sc *subContainer) AddAttachment(attachment attachment) { +func (sc *subContainer) addAttachment(attachment attachment) { sc.Attachments = append(sc.Attachments, attachment) } -func (sc *subContainer) GetSteps() []stepObject { +func (sc *subContainer) getSteps() []stepObject { return sc.Steps } -func (sc *subContainer) AddStep(step stepObject) { +func (sc *subContainer) addStep(step stepObject) { sc.Steps = append(sc.Steps, step) } +func (sc *subContainer) getStatusDetails() *statusDetails { + return sc.StatusDetails +} + +func (sc *subContainer) setStatusDetails(details statusDetails) { + sc.StatusDetails = &details +} + +func (sc *subContainer) setStatus(status string) { + sc.Status = status +} + +func (sc *subContainer) getStatus() string { + return sc.Status +} + func getCurrentTestPhaseObject(t *testing.T) *testPhaseContainer { var currentPhaseObject *testPhaseContainer if phaseContainer, ok := testPhaseObjects[t.Name()]; ok { From 5acb3b5dd7d395f3755b49a9179cb5ed96904549 Mon Sep 17 00:00:00 2001 From: Stanislav Fedii Date: Wed, 22 Jan 2020 16:06:28 -0500 Subject: [PATCH 06/10] Merged the changes from the variadic functions update and integrated the approach with setup/teardown. --- after.go | 18 +++-------- attachment.go | 10 +++--- before.go | 19 +++-------- error.go | 4 +-- example/example_before_after_test.go | 42 +++++++++++++++++-------- hasattachments.go | 6 ---- hasstatus.go | 6 ---- hasstatusdetails.go | 6 ---- hassteps.go | 8 ----- init.go | 12 +++---- interfaces.go | 5 +++ misc.go | 16 +++++----- result.go | 8 ++--- test_phase_container.go | 47 ++++++++++++++++++++++------ 14 files changed, 106 insertions(+), 101 deletions(-) delete mode 100644 hasattachments.go delete mode 100644 hasstatus.go delete mode 100644 hasstatusdetails.go delete mode 100644 hassteps.go diff --git a/after.go b/after.go index 547f46d..bd36db9 100644 --- a/after.go +++ b/after.go @@ -6,19 +6,16 @@ import ( "testing" ) -// AfterTestWithParameters executes a teardown phase of the test and adds parameters to the Allure container object -func AfterTestWithParameters(t *testing.T, description string, parameters map[string]interface{}, labels TestLabels, testFunc func()) { +// AfterTest executes a teardown phase of the test and adds parameters to the Allure container object +func AfterTest(t *testing.T, testOptions ...Option) { after := newAfter() afterSubContainer := after.Afters[0] after.UUID = generateUUID() afterSubContainer.Start = getTimestampMs() - after.Name = t.Name() - - after.Description = description afterSubContainer.Steps = make([]stepObject, 0) - if parameters == nil || len(parameters) > 0 { - afterSubContainer.Parameters = convertMapToParameters(parameters) + for _, option := range testOptions { + option(afterSubContainer) } defer func() { @@ -42,12 +39,7 @@ func AfterTestWithParameters(t *testing.T, description string, parameters map[st testResultKey: afterSubContainer, nodeKey: afterSubContainer, testInstanceKey: t, - }, testFunc) -} - -//AfterTest executes the teardown phase of the test and creates an Allure container object used by Allure reports -func AfterTest(t *testing.T, description string, testFunc func()) { - AfterTestWithParameters(t, description, nil, TestLabels{}, testFunc) + }, afterSubContainer.Action) } func newAfter() *container { diff --git a/attachment.go b/attachment.go index 938c425..6f877d0 100644 --- a/attachment.go +++ b/attachment.go @@ -32,16 +32,16 @@ func AddAttachment(name string, mimeType MimeType, content []byte) error { } func (a *attachment) writeAttachmentFile() error { - resultsPathEnv := os.Getenv(ResultsPathEnvKey) + resultsPathEnv := os.Getenv(resultsPathEnvKey) if resultsPathEnv == "" { - log.Printf("%s environment variable cannot be empty\n", ResultsPathEnvKey) + log.Printf("%s environment variable cannot be empty\n", resultsPathEnvKey) } - if ResultsPath == "" { - ResultsPath = fmt.Sprintf("%s/allure-results", resultsPathEnv) + if resultsPath == "" { + resultsPath = fmt.Sprintf("%s/allure-results", resultsPathEnv) } a.Source = fmt.Sprintf("%s-attachment", a.uuid) - err := ioutil.WriteFile(fmt.Sprintf("%s/%s-attachment", ResultsPath, a.uuid), a.content, 0777) + err := ioutil.WriteFile(fmt.Sprintf("%s/%s-attachment", resultsPath, a.uuid), a.content, 0777) if err != nil { return errors.Wrap(err, "Failed to write in file") } diff --git a/before.go b/before.go index 4fb1a59..462bde1 100644 --- a/before.go +++ b/before.go @@ -6,8 +6,8 @@ import ( "testing" ) -// BeforeTestWithParameters executes a setup phase of the test and adds parameters to the Allure container object -func BeforeTestWithParameters(t *testing.T, description string, parameters map[string]interface{}, labels TestLabels, testFunc func()) { +// BeforeTest executes a setup phase of the test and adds parameters to the Allure container object +func BeforeTest(t *testing.T, testOptions ...Option) { testPhaseObject := getCurrentTestPhaseObject(t) if testPhaseObject.Test != nil { log.Printf("Test's \"%s\" allure setup is being executed after allure test!\n", t.Name()) @@ -18,13 +18,9 @@ func BeforeTestWithParameters(t *testing.T, description string, parameters map[s before.UUID = generateUUID() beforeSubContainer.Start = getTimestampMs() - before.Name = t.Name() - - before.Description = description - beforeSubContainer.Steps = make([]stepObject, 0) - if parameters == nil || len(parameters) > 0 { - beforeSubContainer.Parameters = convertMapToParameters(parameters) + for _, option := range testOptions { + option(beforeSubContainer) } defer func() { @@ -37,12 +33,7 @@ func BeforeTestWithParameters(t *testing.T, description string, parameters map[s testResultKey: beforeSubContainer, nodeKey: beforeSubContainer, testInstanceKey: t, - }, testFunc) -} - -//BeforeTest executes the setup phase of the test and creates an Allure container object used by Allure reports -func BeforeTest(t *testing.T, description string, testFunc func()) { - BeforeTestWithParameters(t, description, nil, TestLabels{}, testFunc) + }, beforeSubContainer.Action) } func newBefore() *container { diff --git a/error.go b/error.go index b5242f4..68d4269 100644 --- a/error.go +++ b/error.go @@ -34,13 +34,13 @@ func allureError(err error, status string, now bool) { manipulateOnObjectFromCtx( testResultKey, func(testResult interface{}) { - testStatusDetails := testResult.(hasStatusDeatils).getStatusDetails() + testStatusDetails := testResult.(hasStatusDetails).getStatusDetails() if testStatusDetails == nil { testStatusDetails = &statusDetails{} } testStatusDetails.Trace = filterStackTrace(debug.Stack()) testStatusDetails.Message = err.Error() - testResult.(hasStatusDeatils).setStatusDetails(*testStatusDetails) + testResult.(hasStatusDetails).setStatusDetails(*testStatusDetails) testResult.(hasStatus).setStatus(status) }) manipulateOnObjectFromCtx( diff --git a/example/example_before_after_test.go b/example/example_before_after_test.go index f1112d6..829dd84 100644 --- a/example/example_before_after_test.go +++ b/example/example_before_after_test.go @@ -7,23 +7,39 @@ import ( ) func TestAllureSetupTeardown(t *testing.T) { - allure.BeforeTest(t, "setup", func() { - allure.Step("Setup step 1", func() {}) - }) + allure.BeforeTest(t, + allure.Description("setup"), + allure.Action(func() { + allure.Step( + allure.Description("Setup step 1"), + allure.Action(func() {})) + })) - allure.Test(t, "actual test", func() { - allure.Step("Test step 1", func() {}) - }) + allure.Test(t, + allure.Description("actual test"), + allure.Action(func() { + allure.Step( + allure.Description("Test step 1"), + allure.Action(func() {})) + })) - allure.AfterTest(t, "teardown", func() { - allure.Step("teardown step 1", func() {}) - }) + allure.AfterTest(t, + allure.Description("teardown"), + allure.Action(func() { + allure.Step( + allure.Description("teardown step 1"), + allure.Action(func() {})) + })) } func TestAllureSetupFailed(t *testing.T) { - allure.BeforeTest(t, "setup failed", func() { - allure.Fail(errors.New("some error")) - }) + allure.BeforeTest(t, + allure.Description("setup failed"), + allure.Action(func() { + allure.Fail(errors.New("some error")) + })) - allure.Test(t, "actual test", func() {}) + allure.Test(t, + allure.Description("actual test"), + allure.Action(func() {})) } diff --git a/hasattachments.go b/hasattachments.go deleted file mode 100644 index e4461a3..0000000 --- a/hasattachments.go +++ /dev/null @@ -1,6 +0,0 @@ -package allure - -type hasAttachments interface { - getAttachments() []attachment - addAttachment(attachment attachment) -} diff --git a/hasstatus.go b/hasstatus.go deleted file mode 100644 index a3448e4..0000000 --- a/hasstatus.go +++ /dev/null @@ -1,6 +0,0 @@ -package allure - -type hasStatus interface { - setStatus(status string) - getStatus() string -} diff --git a/hasstatusdetails.go b/hasstatusdetails.go deleted file mode 100644 index d6894e6..0000000 --- a/hasstatusdetails.go +++ /dev/null @@ -1,6 +0,0 @@ -package allure - -type hasStatusDeatils interface { - getStatusDetails() *statusDetails - setStatusDetails(details statusDetails) -} diff --git a/hassteps.go b/hassteps.go deleted file mode 100644 index bd06707..0000000 --- a/hassteps.go +++ /dev/null @@ -1,8 +0,0 @@ -package allure - -// This interface provides functions required to manipulate children step records, used in the result object and -// step object for recursive handling -type hasSteps interface { - getSteps() []stepObject - addStep(step stepObject) -} diff --git a/init.go b/init.go index c35e130..7b57377 100644 --- a/init.go +++ b/init.go @@ -8,16 +8,16 @@ import ( var ( ctxMgr *gls.ContextManager wsd string - ResultsPath string - CreateFolderOnce sync.Once - CopyEnvFileOnce sync.Once + resultsPath string + createFolderOnce sync.Once + copyEnvFileOnce sync.Once testPhaseObjects map[string]*testPhaseContainer ) const ( - ResultsPathEnvKey = "ALLURE_RESULTS_PATH" - WsPathEnvKey = "ALLURE_WORKSPACE_PATH" - EnvFileKey = "ALLURE_ENVIRONMENT_FILE_PATH" + resultsPathEnvKey = "ALLURE_RESULTS_PATH" + wsPathEnvKey = "ALLURE_WORKSPACE_PATH" + envFileKey = "ALLURE_ENVIRONMENT_FILE_PATH" nodeKey = "current_step_container" testResultKey = "test_result_object" testInstanceKey = "test_instance" diff --git a/interfaces.go b/interfaces.go index 8e73482..18978b4 100644 --- a/interfaces.go +++ b/interfaces.go @@ -26,3 +26,8 @@ type hasAttachments interface { getAttachments() []attachment addAttachment(attachment attachment) } + +type hasStatusDetails interface { + getStatusDetails() *statusDetails + setStatusDetails(details statusDetails) +} diff --git a/misc.go b/misc.go index 753f1da..da6f30a 100644 --- a/misc.go +++ b/misc.go @@ -64,14 +64,14 @@ func getTimestampMs() int64 { } func createFolderIfNotExists() { - resultsPathEnv := os.Getenv(ResultsPathEnvKey) + resultsPathEnv := os.Getenv(resultsPathEnvKey) if resultsPathEnv == "" { - log.Printf("environment variable %s cannot be empty\n", ResultsPathEnvKey) + log.Printf("environment variable %s cannot be empty\n", resultsPathEnvKey) } - ResultsPath = fmt.Sprintf("%s/allure-results", resultsPathEnv) + resultsPath = fmt.Sprintf("%s/allure-results", resultsPathEnv) - if _, err := os.Stat(ResultsPath); os.IsNotExist(err) { - err = os.Mkdir(ResultsPath, 0777) + if _, err := os.Stat(resultsPath); os.IsNotExist(err) { + err = os.Mkdir(resultsPath, 0777) if err != nil { log.Println(err, "Failed to create allure-results folder") } @@ -79,10 +79,10 @@ func createFolderIfNotExists() { } func copyEnvFileIfExists() { - if envFilePath := os.Getenv(EnvFileKey); envFilePath != "" { + if envFilePath := os.Getenv(envFileKey); envFilePath != "" { envFilesStrings := strings.Split(envFilePath, "/") - if ResultsPath != "" { - if _, err := copy(envFilePath, ResultsPath+"/"+envFilesStrings[len(envFilesStrings)-1]); err != nil { + if resultsPath != "" { + if _, err := copy(envFilePath, resultsPath+"/"+envFilesStrings[len(envFilesStrings)-1]); err != nil { log.Println("Could not copy the environment file", err) } } diff --git a/result.go b/result.go index 62db476..4d5fffa 100644 --- a/result.go +++ b/result.go @@ -95,7 +95,7 @@ func (r *result) setStatusDetails(details statusDetails) { } func (r *result) setDefaultLabels(t *testing.T) { - wsd := os.Getenv(WsPathEnvKey) + wsd := os.Getenv(wsPathEnvKey) programCounters := make([]uintptr, 10) callersCount := runtime.Callers(0, programCounters) @@ -134,14 +134,14 @@ func (r *result) addLabel(name string, value string) { } func (r *result) writeResultsFile() error { - CreateFolderOnce.Do(createFolderIfNotExists) - CopyEnvFileOnce.Do(copyEnvFileIfExists) + createFolderOnce.Do(createFolderIfNotExists) + copyEnvFileOnce.Do(copyEnvFileIfExists) j, err := json.Marshal(r) if err != nil { return errors.Wrap(err, "Failed to marshall result into JSON") } - err = ioutil.WriteFile(fmt.Sprintf("%s/%s-result.json", ResultsPath, r.UUID), j, 0777) + err = ioutil.WriteFile(fmt.Sprintf("%s/%s-result.json", resultsPath, r.UUID), j, 0777) if err != nil { return errors.Wrap(err, "Failed to write in file") } diff --git a/test_phase_container.go b/test_phase_container.go index df9776d..0ff8d4c 100644 --- a/test_phase_container.go +++ b/test_phase_container.go @@ -37,7 +37,42 @@ type subContainer struct { Stop int64 `json:"stop,omitempty"` Steps []stepObject `json:"steps,omitempty"` Attachments []attachment `json:"attachments,omitempty"` - Parameters []Parameter `json:"parameters,omitempty"` + Parameters []parameter `json:"parameters,omitempty"` + Action func() `json:"-"` +} + +func (sc *subContainer) addLabel(key string, value string) { + panic("implement me") +} + +func (sc *subContainer) addDescription(description string) { + sc.Description = description +} + +func (sc *subContainer) addParameter(name string, value interface{}) { + sc.Parameters = append(sc.Parameters, parseParameter(name, value)) +} + +func (sc *subContainer) addParameters(parameters map[string]interface{}) { + for key, value := range parameters { + sc.Parameters = append(sc.Parameters, parseParameter(key, value)) + } +} + +func (sc *subContainer) addName(name string) { + sc.Name = name +} + +func (sc *subContainer) addAction(action func()) { + sc.Action = action +} + +func (sc *subContainer) addReason(reason string) { + testStatusDetails := sc.StatusDetails + if testStatusDetails == nil { + testStatusDetails = &statusDetails{} + } + sc.StatusDetails.Message = reason } func (sc *subContainer) getAttachments() []attachment { @@ -87,14 +122,6 @@ func getCurrentTestPhaseObject(t *testing.T) *testPhaseContainer { return currentPhaseObject } -//func getCurrentTestUUID(t *testing.T) string { -// if currentTest := getCurrentTestPhaseObject(t).Test; currentTest != nil { -// return currentTest.UUID -// } else { -// return "" -// } -//} - func (c container) writeResultsFile() error { createFolderOnce.Do(createFolderIfNotExists) copyEnvFileOnce.Do(copyEnvFileIfExists) @@ -114,6 +141,6 @@ func newHelper() *subContainer { return &subContainer{ Steps: make([]stepObject, 0), Attachments: make([]attachment, 0), - Parameters: make([]Parameter, 0), + Parameters: make([]parameter, 0), } } From a81c14b328ff8a5da1de3018beb87b5405a8aa08 Mon Sep 17 00:00:00 2001 From: Stanislav Fedii Date: Wed, 22 Jan 2020 17:57:05 -0500 Subject: [PATCH 07/10] Started re-arranging the readme file. --- README.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d494a52..47d74e0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,18 @@ allure-go is a Go package that provides support for Allure reports in Golang : https://github.com/allure-framework/allure2 -## Installing +## Table of Contents + +1. [Installation](#installation) +2. [Usage](#usage) + 1. [Specifying allure-results folder location](#specifying-allure-results-folder-location) + 2. [Using environment file](#using-environment-file) + 3. [Examples](#examples) +3. [Optional environment variable](#optional-environment-variable) +4. [Features](#features-available) +5. [Improvements needed](#improvements-needed) + +## Installation To start using allure-go, install Go and run `go get`: @@ -69,8 +80,9 @@ You will now get the relative path of your test files from your project root lev This project is still in progress. Here is the list of available features : - It is possible to create a test object in the report with the `allure.Test()` method. - It is possible to create a step object inside a test object of the report with the `allure.Step()` method. -- It is possible to pass parameters to a test object or a step object with the `allure.TestWithParameters()` and `allure.StepWithParameters()` methods. +- It is possible to pass parameters to a test object or a step object. - It is possible to add attachments to a test object or a step object with the `allure.AddAttachment()` method (depending if it is called inside an `allure.Test()` wrapper or an `allure.Step()` wrapper). +- Setup and Teardown phases are available and can be specified with `allure.BeforeTest()` and `allure.AfterTest()` functions accordingly. ## Improvements needed @@ -85,4 +97,5 @@ Here is the list of improvements that are needed for existing features : - [ ] Add support of Flaky tag - [X] Add support of Categories - [X] Add support of Features -- [X] Add support for environment files \ No newline at end of file +- [X] Add support for environment files +- [ ] Add support for history \ No newline at end of file From edab6a8fd77a8f58708c2d9d83f0c20237d8b6a5 Mon Sep 17 00:00:00 2001 From: Stanislav Fedii Date: Thu, 23 Jan 2020 17:47:20 -0500 Subject: [PATCH 08/10] Updated README.md --- README.md | 261 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 216 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 47d74e0..b0eeee3 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,263 @@ -# Allure Golang Integrations +# Allure Golang Integration -allure-go is a Go package that provides support for Allure reports in Golang : https://github.com/allure-framework/allure2 +`allure-go` is a Go package that provides all required tools to transform your tests into [Allure](https://github.com/allure-framework/allure2) reports. ## Table of Contents 1. [Installation](#installation) 2. [Usage](#usage) - 1. [Specifying allure-results folder location](#specifying-allure-results-folder-location) - 2. [Using environment file](#using-environment-file) - 3. [Examples](#examples) -3. [Optional environment variable](#optional-environment-variable) -4. [Features](#features-available) -5. [Improvements needed](#improvements-needed) + 1. [Specifying `allure-results` folder location](#specifying-allure-results-folder-location) + 2. [Optional environment variable](#optional-environment-variable) +3. [Features](#features) + 1. [Nested Steps](#nested-steps) + 2. [Option Slices](#option-slices) + 1. [Description](#description) + 2. [Action](#action) + 3. [Parameter](#parameter) + 4. [Parameters](#parameters) + 3. [Test-specific options](#test-specific-options) + 1. [Lead](#lead) + 2. [Owner](#owner) + 3. [Epic](#epic) + 4. [Severity](#severity) + 5. [Story](#story) + 6. [Feature](#feature) + 3. [Parameters](#parameters-as-a-feature) + 4. [Attachments](#attachments) + 5. [Setup/Teardown](#setupteardown) + 6. [Environment files](#environment-files) +4. [Feature Roadmap](#feature-roadmap) -## Installation -To start using allure-go, install Go and run `go get`: +## Installation +In order to retrieve `allure-go` simply run the following command (assuming `Go` is installed): ```sh $ go get -u github.com/dailymotion/allure-go ``` -This will retrieve the library. - ## Usage ### Specifying allure-results folder location -First of all, you need to set an environment variable to define the `allure-results` folder location: +Golang's approach to evaluating test scripts does not allow to establish a single location for test results +programmatically, the following environment variable is required for `allure-go` to work as expected. +In order to specify the location of a report run the following: ``` -export ALLURE_RESULTS_PATH=/some/path +export ALLURE_RESULTS_PATH= ``` -This should be the path where the `allure-results` folder exists or should be created, not the path of the folder itself. -The `allure-results` folder is automatically created if it doesn't exist with `drwxr-xr-x` file system permission. +`allure-results` folder will be created in that location. -### Using environment file +### Optional environment variable -Allure reporting tool allows putting run-specific properties in the report provided -by `environment.properties` or `environment.xml` file. +Allure-go will retrieve the absolute path of your testing files (for example, /Users/myuser/Dev/myProject/tests/myfile_test.go) and will display this path in the reports. -In order to take advantage of this feature, set an environment variable to specify the location of the file: +To make it cleaner, you can trim prefix the path of your project by defining the `ALLURE_WORKSPACE_PATH` with the value of your project root path : +``` +export ALLURE_WORKSPACE_PATH=/another/path ``` -export ALLURE_ENVIRONMENT_FILE_PATH=/path/to/file/environment.properties -``` -### Examples +You will now get the relative path of your test files from your project root level. + +## Features -To implement this library in your tests, follow the [examples](example/example_test.go). +### Nested Steps -Execute tests with the usual go test command : +`allure-go` takes advantage of Allure Reports feature of nested steps and allows marking portions of the code as steps +even if said portions are found outside test scripts in functions that are eventually called by a test script. +This is a great feature for enabling levels of detail for test reporting. ``` -go test ./example +func TestStep(t *testing.T) { + allure.Step(allure.Description("Action"), + allure.Action(func() { + fmt.Println("This block of code is a step") + })) + PerformAction() +} + +func PerformAction() { + allure.Step(allure.Description("Action"), + allure.Action(func() { + fmt.Println("This block of code is also a step") + })) +} ``` -This will automatically generate an `allure-results` folder in the path defined by ALLURE_RESULTS_PATH. +### Option Slices -To see the report in html, generate it with the allure command line : +Option slices allow providing as much or as little detail for the test scripts as needed. +#### Description ``` -allure serve $ALLURE_RESULTS_PATH/allure-results +allure.Description("Description of a test or a step") ``` -This will automatically generate and open the HTML reports in a browser. +Provides a name for the step and a description for the test. This option is required + +#### Action +``` +allure.Action(func() {}) +``` +This option is a wrapper for the portion of code that is either your test or your step. This option is required. -The results file are compatible with the [Jenkins plugin](https://wiki.jenkins.io/display/JENKINS/Allure+Plugin). +#### Parameter +``` +allure.Parameter(name string, value interface{}) +``` +This option specifies parameters for the test or step accordingly. +This particular option can be called multiple times to add multiple parameters. -## Optional environment variable +#### Parameters +``` +allure.Parameters(parameters map[string]interface{}) +``` +This option allows specifying multiple parameters at once. +This option can be called multiple times as well and will not interfere with previous parameter assignments. -Allure-go will retrieve the absolute path of your testing files (for example, /Users/myuser/Dev/myProject/tests/myfile_test.go) and will display this path in the reports. +### Test-specific options -To make it cleaner, you can trim prefix the path of your project by defining the `ALLURE_WORKSPACE_PATH` with the value of your project root path : +#### Lead ``` -export ALLURE_WORKSPACE_PATH=/another/path +allure.Lead(lead string) ``` +This option specifies a lead of the feature. -You will now get the relative path of your test files from your project root level. +#### Owner +``` +allure.Owner(owner string) +``` +This option specifies an owner of the feature. -## Features available +#### Epic +``` +allure.Epic(epic string) +``` +This option specifies an epic this test belongs to. -This project is still in progress. Here is the list of available features : -- It is possible to create a test object in the report with the `allure.Test()` method. -- It is possible to create a step object inside a test object of the report with the `allure.Step()` method. -- It is possible to pass parameters to a test object or a step object. -- It is possible to add attachments to a test object or a step object with the `allure.AddAttachment()` method (depending if it is called inside an `allure.Test()` wrapper or an `allure.Step()` wrapper). -- Setup and Teardown phases are available and can be specified with `allure.BeforeTest()` and `allure.AfterTest()` functions accordingly. +#### Severity +``` +allure.Severity(severity severity.Severity) +``` +This option specifies a severity level of the test script to allow better prioritization. + +#### Story +``` +allure.Story(story string) +``` +This option specifies a story this test script belongs to. +This particular option can be called multiple times to connect this test script to multiple stories. + +#### Feature +``` +allure.Feature(feature string) +``` +This option specifies a feature this test script belongs to. +This particular option can be called multiple times to connect this test script to multiple features. + +### Parameters as a feature +`allure-go` allows specifying parameters on both test and step level to further improvement informativeness of your +test scripts and bring it one step closer to being documentation. +In order to specify a parameter refer to the following example: +``` +func TestParameter(t *testing.T) { + allure.Test(allure.Description("Test that has parameters"), + allure.Parameter("testParameter", "test"), + allure.Action(func() { + allure.Step(allure.Description("Step with parameters"), + allure.Action(func() {}), + allure.Parameter("stepParameter", "step")) + }) + ) +} +``` +Allure parameters integrate neatly with Golang's parameterized tests too: +``` +func TestParameterized(t *testing.T) { + for i := 0; i < 5; i++ { + t.Run("", func(t *testing.T) { + allure.Test(t, + allure.Description("Test with parameters"), + allure.Parameter("counter", i), + allure.Action(func() {})) + }) + } +} +``` + +### Attachments +`allure-go` providing an ability to attach content of various MIME types to test scripts and steps. +The most common MIME types are available as constants in `allure-go` +(`image/png`, `application/json`, `text/plain` and `video/mpeg`). +In order to attach content to a test or step refer to the following example: +``` +func TestImageAttachmentToStep(t *testing.T) { + allure.Test(t, + allure.Description("testing image attachment"), + allure.Action(func() { + allure.Step(allure.Description("adding an image attachment"), + allure.Action(func() { + err := allure.AddAttachment("image", allure.ImagePng, ) + if err != nil { + log.Println(err) + } + })) + })) +} +``` + +### Setup/Teardown +Golang does not directly follow the setup/teardown approach of other languages like Java, C# and Python. +This does not prevent us from logically separating said phases in test scripts and taking +advantage of separating these phases in the test reports too. +`allure.BeforeTest` and `allure.AfterTest` functions have to be called in sequence with the test wrapper. + +`allure.BeforeTest` goes first, then `allure.Test` and finally `allure.AfterTest`. +This is done to allow various logical conclusions, such as upon failure of `allure.BeforeTest` actual test body +will be skipped, etc. + +Please refer to the following example of setup/teardown usage: +``` +func TestAllureSetupTeardown(t *testing.T) { + allure.BeforeTest(t, + allure.Description("setup"), + allure.Action(func() { + allure.Step( + allure.Description("Setup step 1"), + allure.Action(func() {})) + })) + + allure.Test(t, + allure.Description("actual test"), + allure.Action(func() { + allure.Step( + allure.Description("Test step 1"), + allure.Action(func() {})) + })) + + allure.AfterTest(t, + allure.Description("teardown"), + allure.Action(func() { + allure.Step( + allure.Description("Teardown step 1"), + allure.Action(func() {})) + })) +} +``` +In case of utilizing setup/teardown in a parameterized test, all of the test phases have to be called +inside `t.Run(...)` call. + +### Environment Files +`allure-go` allows taking advantage of the environment file feature of Allure reports. You can specify report-specific +variables that you want to appear in the report such as browser kind and version, OS, environment name, etc. +In order to do that create an `environment.xml` or `environment.properties` file as instructed [here](https://docs.qameta.io/allure/#_environment) and define an +environment variable for `allure-go` to incorporate in the results: +``` +export ALLURE_ENVIRONMENT_FILE_PATH= +``` -## Improvements needed +## Feature Roadmap +`allure-go` is still in active development and is not yet stabilized or finalized. -Here is the list of improvements that are needed for existing features : +Here is a list of improvements that are needed for existing features : - [X] Manage failure at step level : the project must be able to mark a Step as failed (red in the report) when an assertion fails. The assertion stacktrace must appear in the report. - [X] Manage error at test and step level : the project must be able to mark a Test and Step as broken (orange in the report) when an error happens. The error stacktrace must appear in the report. - [X] Add support of Links From d7bef4f4a9feca943445ef4c4b9e19fc2c604757 Mon Sep 17 00:00:00 2001 From: Stanislav Fedii Date: Thu, 23 Jan 2020 17:55:41 -0500 Subject: [PATCH 09/10] Updated README.md --- README.md | 81 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index b0eeee3..f223a75 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ $ go get -u github.com/dailymotion/allure-go Golang's approach to evaluating test scripts does not allow to establish a single location for test results programmatically, the following environment variable is required for `allure-go` to work as expected. In order to specify the location of a report run the following: -``` +```shell script export ALLURE_RESULTS_PATH= ``` @@ -54,7 +54,7 @@ export ALLURE_RESULTS_PATH= Allure-go will retrieve the absolute path of your testing files (for example, /Users/myuser/Dev/myProject/tests/myfile_test.go) and will display this path in the reports. To make it cleaner, you can trim prefix the path of your project by defining the `ALLURE_WORKSPACE_PATH` with the value of your project root path : -``` +```shell script export ALLURE_WORKSPACE_PATH=/another/path ``` @@ -67,7 +67,15 @@ You will now get the relative path of your test files from your project root lev `allure-go` takes advantage of Allure Reports feature of nested steps and allows marking portions of the code as steps even if said portions are found outside test scripts in functions that are eventually called by a test script. This is a great feature for enabling levels of detail for test reporting. -``` +```go +package test + +import ( + "fmt" + "github.com/dailymotion/allure-go" + "testing" +) + func TestStep(t *testing.T) { allure.Step(allure.Description("Action"), allure.Action(func() { @@ -88,26 +96,26 @@ func PerformAction() { Option slices allow providing as much or as little detail for the test scripts as needed. #### Description -``` +```go allure.Description("Description of a test or a step") ``` Provides a name for the step and a description for the test. This option is required #### Action -``` +```go allure.Action(func() {}) ``` This option is a wrapper for the portion of code that is either your test or your step. This option is required. #### Parameter -``` +```go allure.Parameter(name string, value interface{}) ``` This option specifies parameters for the test or step accordingly. This particular option can be called multiple times to add multiple parameters. #### Parameters -``` +```go allure.Parameters(parameters map[string]interface{}) ``` This option allows specifying multiple parameters at once. @@ -116,38 +124,38 @@ This option can be called multiple times as well and will not interfere with pre ### Test-specific options #### Lead -``` +```go allure.Lead(lead string) ``` This option specifies a lead of the feature. #### Owner -``` +```go allure.Owner(owner string) ``` This option specifies an owner of the feature. #### Epic -``` +```go allure.Epic(epic string) ``` This option specifies an epic this test belongs to. #### Severity -``` +```go allure.Severity(severity severity.Severity) ``` This option specifies a severity level of the test script to allow better prioritization. #### Story -``` +```go allure.Story(story string) ``` This option specifies a story this test script belongs to. This particular option can be called multiple times to connect this test script to multiple stories. #### Feature -``` +```go allure.Feature(feature string) ``` This option specifies a feature this test script belongs to. @@ -157,20 +165,34 @@ This particular option can be called multiple times to connect this test script `allure-go` allows specifying parameters on both test and step level to further improvement informativeness of your test scripts and bring it one step closer to being documentation. In order to specify a parameter refer to the following example: -``` +```go +package test + +import ( + "github.com/dailymotion/allure-go" + "testing" + ) + func TestParameter(t *testing.T) { - allure.Test(allure.Description("Test that has parameters"), + allure.Test(t, + allure.Description("Test that has parameters"), allure.Parameter("testParameter", "test"), allure.Action(func() { allure.Step(allure.Description("Step with parameters"), allure.Action(func() {}), allure.Parameter("stepParameter", "step")) - }) - ) + })) } ``` Allure parameters integrate neatly with Golang's parameterized tests too: -``` +```go +package test + +import ( + "github.com/dailymotion/allure-go" + "testing" + ) + func TestParameterized(t *testing.T) { for i := 0; i < 5; i++ { t.Run("", func(t *testing.T) { @@ -188,14 +210,23 @@ func TestParameterized(t *testing.T) { The most common MIME types are available as constants in `allure-go` (`image/png`, `application/json`, `text/plain` and `video/mpeg`). In order to attach content to a test or step refer to the following example: -``` +```go +package test + +import ( + "errors" + "github.com/dailymotion/allure-go" + "log" + "testing" + ) + func TestImageAttachmentToStep(t *testing.T) { allure.Test(t, allure.Description("testing image attachment"), allure.Action(func() { allure.Step(allure.Description("adding an image attachment"), allure.Action(func() { - err := allure.AddAttachment("image", allure.ImagePng, ) + err := allure.AddAttachment("image", allure.ImagePng, []byte("")) if err != nil { log.Println(err) } @@ -215,7 +246,13 @@ This is done to allow various logical conclusions, such as upon failure of `allu will be skipped, etc. Please refer to the following example of setup/teardown usage: -``` +```go +package test + +import ( + "github.com/dailymotion/allure-go" + "testing" +) func TestAllureSetupTeardown(t *testing.T) { allure.BeforeTest(t, allure.Description("setup"), @@ -250,7 +287,7 @@ inside `t.Run(...)` call. variables that you want to appear in the report such as browser kind and version, OS, environment name, etc. In order to do that create an `environment.xml` or `environment.properties` file as instructed [here](https://docs.qameta.io/allure/#_environment) and define an environment variable for `allure-go` to incorporate in the results: -``` +```shell script export ALLURE_ENVIRONMENT_FILE_PATH= ``` From 25a235bb64eb332b4be180b3942aa60e986b95c8 Mon Sep 17 00:00:00 2001 From: Stanislav Fedii Date: Fri, 24 Jan 2020 11:12:28 -0500 Subject: [PATCH 10/10] Added description of `allure.Test` function call and added missing `allure.Test` function call in `allure.Step` description. --- README.md | 54 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index f223a75..7698cf5 100644 --- a/README.md +++ b/README.md @@ -9,28 +9,28 @@ 1. [Specifying `allure-results` folder location](#specifying-allure-results-folder-location) 2. [Optional environment variable](#optional-environment-variable) 3. [Features](#features) - 1. [Nested Steps](#nested-steps) - 2. [Option Slices](#option-slices) + 1. [Test Wrapper](#test-wrapper) + 2. [Nested Steps](#nested-steps) + 3. [Option Slices](#option-slices) 1. [Description](#description) 2. [Action](#action) 3. [Parameter](#parameter) 4. [Parameters](#parameters) - 3. [Test-specific options](#test-specific-options) + 4. [Test-specific options](#test-specific-options) 1. [Lead](#lead) 2. [Owner](#owner) 3. [Epic](#epic) 4. [Severity](#severity) 5. [Story](#story) 6. [Feature](#feature) - 3. [Parameters](#parameters-as-a-feature) - 4. [Attachments](#attachments) - 5. [Setup/Teardown](#setupteardown) - 6. [Environment files](#environment-files) + 4. [Parameters](#parameters-as-a-feature) + 5. [Attachments](#attachments) + 6. [Setup/Teardown](#setupteardown) + 7. [Environment files](#environment-files) 4. [Feature Roadmap](#feature-roadmap) ## Installation - In order to retrieve `allure-go` simply run the following command (assuming `Go` is installed): ```sh $ go get -u github.com/dailymotion/allure-go @@ -39,7 +39,6 @@ $ go get -u github.com/dailymotion/allure-go ## Usage ### Specifying allure-results folder location - Golang's approach to evaluating test scripts does not allow to establish a single location for test results programmatically, the following environment variable is required for `allure-go` to work as expected. In order to specify the location of a report run the following: @@ -50,7 +49,6 @@ export ALLURE_RESULTS_PATH= `allure-results` folder will be created in that location. ### Optional environment variable - Allure-go will retrieve the absolute path of your testing files (for example, /Users/myuser/Dev/myProject/tests/myfile_test.go) and will display this path in the reports. To make it cleaner, you can trim prefix the path of your project by defining the `ALLURE_WORKSPACE_PATH` with the value of your project root path : @@ -62,8 +60,30 @@ You will now get the relative path of your test files from your project root lev ## Features -### Nested Steps +### Test Wrapper +`allure-go` test reporting relies on wrapping test scripts inside a call to `allure.Test()` function: +```go +allure.Test(t *testing.T, testOptions ...Option) +``` +This function allows adding required modules representing a test along with additional non-essential modules like labels. +For basic usage refer to the following example: +```go +package test + +import ( + "fmt" + "github.com/dailymotion/allure-go" + "testing" +) + +func TestStep(t *testing.T) { + allure.Test(t, allure.Action(func() { + fmt.Println("This block of code is a test") + })) +} +``` +### Nested Steps `allure-go` takes advantage of Allure Reports feature of nested steps and allows marking portions of the code as steps even if said portions are found outside test scripts in functions that are eventually called by a test script. This is a great feature for enabling levels of detail for test reporting. @@ -77,11 +97,13 @@ import ( ) func TestStep(t *testing.T) { - allure.Step(allure.Description("Action"), - allure.Action(func() { - fmt.Println("This block of code is a step") + allure.Test(t, allure.Action(func() { + allure.Step(allure.Description("Action"), + allure.Action(func() { + fmt.Println("This block of code is a step") + })) + PerformAction() })) - PerformAction() } func PerformAction() { @@ -93,8 +115,8 @@ func PerformAction() { ``` ### Option Slices - Option slices allow providing as much or as little detail for the test scripts as needed. + #### Description ```go allure.Description("Description of a test or a step")