Allure-Go - the project that provides a complete allure provider in go, without overloading the interface of usage.
The project started as a fork of testify, but over time it got its own runner and its own features.
- 🎓 Head of contents
- ⚡ Features
- 🔰 Getting Started with framework!
- 🔧 Configure your environment!
- 😏 Going Deeper...
- 🎒 Few more examples
Providing a separate package allows you to customize your work with allure.
Release v0.6.17
Now you can improve your reports with t.WithTestSetup(func (provider.T))
or t.WithTeardown(func (provider.T))
.
If you need some more setup customization, now you can pass your steps to Set up
and Tear down
right from the test!
Check out our example for more information, or check out example below:
ℹ️ Feature works only with Test. If you try to run it in Before/After hook or inside itself - nothing will happen.
ℹ️ Yes, it works with t.Parallel, but HIGHLY recommended to run t.Parallel AFTER TestSetup.
ℹ️ Yes, it works with TableTest.
Release v0.6.16
New absolutely amazing way to build your table tests. Have a look at here or here.
ℹ️ you need just create parameter field in suite struct and add argument to the test signature (pretty cool, hah?).
-
❓ Can I use this amazing feature with my common tests?
-
ℹ️ YES, it works with any other type of suite's tests.
-
❓Can I use structs, pointers or interfaces as parameters?
-
ℹ️ YES you can (and, I guess, you should) use it with structs and interfaces as params.
-
❓ Can I use it with
TestRunner
object? -
ℹ️ NO,
TestRunner
object doesn't support this.
Now suite.RunSuite
and runner.RunTests
returns new adorable struct SuiteResult
to customize your integrations.
ℹ️ SuiteResult
contains information about suite Container
and each test's Container
and Result
.
The package containing the data model for Allure.
Complete list of allure objects:
Attachment
Container
Label
Link
Parameter
Result
Step
The package provides a fully integrated with Allure JUNIT-like framework for working with tests.
Main features:
✅ Allure support
- Test plan support (Allure TestOps feature)
- Tests as code
- Extensive configuration options for test steps
- Testify's asserts already wrapped with
allure.Step
! - xSkip support (you can mark test as
t.XSkip()
and it will be skipped on fail)
✅ Suite support
- Before/After feature
- Suite as go-struct
- Suite as sub-test
✅ Parallel running
- Parallel tests in suite structs
- Parallel steps in test functions
Step 0. Install package
go get github.com/ozontech/allure-go/pkg/framework
NOTE: No suite tests doesn't support before after hooks
Step 1. Describe tests
package test
import (
"testing"
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/runner"
)
func TestRunner(t *testing.T) {
runner.Run(t, "My first test", func(t provider.T) {
t.NewStep("My First Step!")
})
runner.Run(t, "My second test", func(t provider.T) {
t.WithNewStep("My Second Step!", func(sCtx provider.StepCtx) {
sCtx.NewStep("My First SubStep!")
})
})
}
Step 2. Run it!
go test ./test/...
Step 1. Make your first test suite
package tests
import (
"github.com/ozontech/allure-go/pkg/framework/suite"
)
type MyFirstSuite struct {
suite.Suite
}
Step 2. Extend it with tests
package tests
import (
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/suite"
)
type MyFirstSuite struct {
suite.Suite
}
func (s *MyFirstSuite) TestMyFirstTest(t provider.T) {
t.NewStep("My First Step!")
}
func (s *MyFirstSuite) TestMySecondTest(t provider.T) {
t.WithNewStep("My Second Step!", func(sCtx provider.StepCtx) {
sCtx.NewStep("My First SubStep!")
})
}
Step 3. Describe suite runner function
package test
import (
"testing"
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/suite"
)
type MyFirstSuite struct {
suite.Suite
}
func (s *MyFirstSuite) TestMyFirstTest(t provider.T) {
t.NewStep("My First Step!")
}
func (s *MyFirstSuite) TestMySecondTest(t provider.T) {
t.WithNewStep("My Second Step!", func(sCtx provider.StepCtx) {
sCtx.NewStep("My First SubStep!")
})
}
func TestSuiteRunner(t *testing.T) {
suite.RunSuite(t, new(MyFirstSuite))
}
Step 4. Run it!
go test ./test/...
Step 1. Init runner object
package test
import (
"testing"
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/runner"
)
func TestRunner(t *testing.T) {
r := runner.NewRunner(t, "My First Suite!")
}
Step 2. Extend it with tests
package test
import (
"testing"
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/runner"
)
func TestRunner(t *testing.T) {
r := runner.NewRunner(t, "My First Suite!")
r.NewTest("My first test", func(t provider.T) {
t.NewStep("My First Step!")
})
r.NewTest("My second test", func(t provider.T) {
t.WithNewStep("My Second Step!", func(sCtx provider.StepCtx) {
sCtx.NewStep("My First SubStep!")
})
})
}
Step 3. Call RunTests function from runner
package test
import (
"testing"
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/runner"
)
func TestRunner(t *testing.T) {
r := runner.NewRunner(t, "My First Suite!")
r.NewTest("My first test", func(t provider.T) {
t.NewStep("My First Step!")
})
r.NewTest("My second test", func(t provider.T) {
t.WithNewStep("My Second Step!", func(sCtx provider.StepCtx) {
sCtx.NewStep("My First SubStep!")
})
})
r.RunTests()
}
Step 4. Run it!
go test ./test/...
The path to allure reports is gathered from the two global variables: ${ALLURE_OUTPUT_FOLDER}/${ALLURE_OUTPUT_PATH}
⚡ The ALLURE_OUTPUT_FOLDER
is the name of the folder where the allure reports will be stored (by
default, allure-results
).
⚡ The ALLURE_OUTPUT_PATH
is the path where the ALLURE_OUTPUT_FOLDER
will be created (by default this is the root
folder root folder of the test launcher).
You can also specify several global configurations to integrate with your TMS or Task Tracker:
⚡ ALLURE_ISSUE_PATTERN
- Specifies the url pattern for your Issues. Has no default value. Mandatory. Must
contain %s
.
If ALLURE_ISSUE_PATTERN
is not specified, the link will be read in its entirety.
Example:
package provider_demo
import (
"testing"
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/runner"
)
func TestSampleDemo(t *testing.T) {
runner.Run(t, "Just Link", func(t provider.T) {
t.SetIssue("https://pkg.go.dev/github.com/stretchr/testify")
})
runner.Run(t, "With Pattern", func(t provider.T) {
_ = os.Setenv("ALLURE_ISSUE_PATTERN", "https://pkg.go.dev/github.com/stretchr/%s")
t.SetIssue("testify")
})
}
⚡ ALLURE_TESTCASE_PATTERN
- Specifies the url pattern for your TestCases. Has no default value. Mandatory. Must
contain %s
.
If ALLURE_TESTCASE_PATTERN
is not specified, the link will be read in its entirety.
Example:
package provider_demo
import (
"testing"
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/runner"
)
func TestSampleDemo(t *testing.T) {
runner.Run(t, "Just Link", func(t provider.T) {
t.SetTestCase("https://pkg.go.dev/github.com/stretchr/testify")
})
runner.Run(t, "With Pattern", func(t provider.T) {
_ = os.Setenv("ALLURE_TESTCASE_PATTERN", "https://pkg.go.dev/github.com/stretchr/%s")
t.SetTestCase("testify")
})
}
⚡ ALLURE_LAUNCH_TAGS
- Sheds a list of tags that will be applied to each test by default. It has no default value.
ℹ️ Tip: ALLURE_LAUNCH_TAGS
- Very handy to use with CI/CD. For example, you can define test groups in it by your
ci-jobs, or you can roll the name of a branch.
⚡ ALLURE_TESTPLAN_PATH
- describes path to your test plan json.
ℹ️ Tip: To use this feature you need to work with Allure TestOps
Cute - is library for simply creating HTTP tests in Go with Allure reports.
Main features:
⚡ Full integration with Allure
⚡ Expressive and intuitive syntax
⚡ Built-in JSON support
⚡ Custom asserts
⚡ One step to BDD
Cute can simply improve your allure-go testing experience! Try it 😉
🚀 YES! We really can run parallel tests in suites.
🚀 Even with async steps.
🚀 Example below.
Test code:
package async
import (
"fmt"
"time"
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/suite"
)
type AsyncSuiteStepDemo struct {
suite.Suite
}
func (s *AsyncSuiteStepDemo) BeforeEach(t provider.T) {
t.Epic("Async")
t.Feature("Async Steps")
t.Tags("async", "suite", "steps")
}
func (s *AsyncSuiteStepDemo) TestAsyncStepDemo1(t provider.T) {
t.Title("Async Test with async steps 1")
t.Parallel()
t.WithNewAsyncStep("Async Step 1", func(ctx provider.StepCtx) {
ctx.WithNewParameters("Start", fmt.Sprintf("%s", time.Now()))
time.Sleep(3 * time.Second)
ctx.WithNewParameters("Stop", fmt.Sprintf("%s", time.Now()))
})
t.WithNewAsyncStep("Async Step 2", func(ctx provider.StepCtx) {
ctx.WithNewParameters("Start", fmt.Sprintf("%s", time.Now()))
time.Sleep(3 * time.Second)
ctx.Logf("Step 2 Stopped At: %s", fmt.Sprintf("%s", time.Now()))
ctx.WithNewParameters("Stop", fmt.Sprintf("%s", time.Now()))
})
}
Allure output:
Test code:
package examples
import (
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/suite"
)
type StepTreeDemoSuite struct {
suite.Suite
}
func (s *StepTreeDemoSuite) TestInnerSteps(t provider.T) {
t.Epic("Demo")
t.Feature("Inner Steps")
t.Title("Simple Nesting")
t.Description(`
Step A is parent step for Step B and Step C
Call order will be saved in allure report
A -> (B, C)`)
t.Tags("Steps", "Nesting")
t.WithNewStep("Step A", func(ctx provider.StepCtx) {
ctx.NewStep("Step B")
ctx.NewStep("Step C")
})
}
Output to Allure:
Test code:
package examples
import (
"encoding/json"
"github.com/ozontech/allure-go/pkg/allure"
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/suite"
)
type JSONStruct struct {
Message string `json:"message"`
}
type AttachmentTestDemoSuite struct {
suite.Suite
}
func (s *AttachmentTestDemoSuite) TestAttachment(t provider.T) {
t.Epic("Demo")
t.Feature("Attachments")
t.Title("Test Attachments")
t.Description(`
Test's test body and all steps inside can contain attachments`)
t.Tags("Attachments", "BeforeAfter", "Steps")
attachmentText := `THIS IS A TEXT ATTACHMENT`
t.Attachment(allure.NewAttachment("Text Attachment if TestAttachment", allure.Text, []byte(attachmentText)))
step := allure.NewSimpleStep("Step A")
var ExampleJson = JSONStruct{"this is JSON message"}
attachmentJSON, _ := json.Marshal(ExampleJson)
step.Attachment(allure.NewAttachment("Json Attachment for Step A", allure.JSON, attachmentJSON))
t.Step(step)
}
Output to Allure:
Test code:
package examples
import (
"testing"
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/suite"
)
type TestRunningDemoSuite struct {
suite.Suite
}
func (s *TestRunningDemoSuite) TestBeforesAfters(t provider.T) {
t.Parallel()
// use RunInner to run suite of tests
s.RunSuite(t, new(BeforeAfterDemoSuite))
}
func (s *TestRunningDemoSuite) TestFails(t provider.T) {
t.Parallel()
s.RunSuite(t, new(FailsDemoSuite))
}
func (s *TestRunningDemoSuite) TestLabels(t provider.T) {
t.Parallel()
s.RunSuite(t, new(LabelsDemoSuite))
}
func TestRunDemo(t *testing.T) {
// use RunSuites to run suite of suites
suite.RunSuite(t, new(TestRunningDemoSuite))
}
Output to Allure:
Test code:
package examples
import (
"testing"
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/suite"
)
type BeforeAfterDemoSuite struct {
suite.Suite
}
func (s *BeforeAfterDemoSuite) BeforeEach(t provider.T) {
t.NewStep("Before Test Step")
}
func (s *BeforeAfterDemoSuite) AfterEach(t provider.T) {
t.NewStep("After Test Step")
}
func (s *BeforeAfterDemoSuite) BeforeAll(t provider.T) {
t.NewStep("Before suite Step")
}
func (s *BeforeAfterDemoSuite) AfterAll(t provider.T) {
t.NewStep("After suite Step")
}
func (s *BeforeAfterDemoSuite) TestBeforeAfterTest(t provider.T) {
t.Epic("Demo")
t.Feature("BeforeAfter")
t.Title("Test wrapped with SetUp & TearDown")
t.Description(`
This test wrapped with SetUp and TearDown containert.`)
t.Tags("BeforeAfter")
}
func TestBeforesAfters(t *testing.T) {
t.Parallel()
suite.RunSuite(t, new(BeforeAfterDemoSuite))
}
Output to Allure:
Test code:
package examples
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/suite"
)
type DemoSuite struct {
suite.Suite
}
func (s *DemoSuite) TestXSkipFail(t provider.T) {
t.Title("This test skipped by assert with message")
t.Description(`
This Test will be skipped with assert Error.
Error text: Assertion Failed`)
t.Tags("fail", "xskip", "assertions")
t.XSkip()
t.Require().Equal(1, 2, "Assertion Failed")
}
func TestDemoSuite(t *testing.T) {
t.Parallel()
suite.RunSuite(t, new(DemoSuite))
}
Output to Allure:
Test code:
package suite_demo
import (
"testing"
"github.com/jackc/fake"
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/suite"
)
type ParametrizedSuite struct {
suite.Suite
ParamCities []string
}
func (s *ParametrizedSuite) BeforeAll(t provider.T) {
for i := 0; i < 10; i++ {
s.ParamCities = append(s.ParamCities, fake.City())
}
}
func (s *ParametrizedSuite) TableTestCities(t provider.T, city string) {
t.Parallel()
t.Require().NotEmpty(city)
}
func TestNewParametrizedDemo(t *testing.T) {
suite.RunSuite(t, new(ParametrizedSuite))
}
Output to Allure:
This feature allows you to extend your test setting up/tearing down functional
Test code:
package suite_demo
import (
"context"
"testing"
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/suite"
)
type SetupSuite struct {
suite.Suite
}
func (s *SetupSuite) TestMyTest(t provider.T) {
var (
v1 string
v2 int
ctx context.Context
)
t.WithTestSetup(func(t provider.T) {
t.WithNewStep("init v1", func(sCtx provider.StepCtx) {
v1 = "string"
sCtx.WithNewParameters("v1", v1)
})
t.WithNewStep("init v2", func(sCtx provider.StepCtx) {
v2 = 123
sCtx.WithNewParameters("v2", v2)
})
t.WithNewStep("init ctx", func(sCtx provider.StepCtx) {
ctx = context.Background()
sCtx.WithNewParameters("ctx", ctx)
})
})
defer t.WithTestTeardown(func(t provider.T) {
t.WithNewStep("Close ctx", func(sCtx provider.StepCtx) {
ctx.Done()
sCtx.WithNewParameters("ctx", ctx)
})
})
}
func TestRunner(t *testing.T) {
suite.RunSuite(t, new(SetupSuite))
}
Allure output:
When suit fails at the setup stage (beforeAll), report will not contain ALLURE_ID
field.
To prevent it you can use GetAllureID(testName string) string
method for common tests and
InitializeTestsParams()
method for parametrized tests.
Example for GetAllureID
method:
package suite_demo
import (
"testing"
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/suite"
)
type AllureIDSuite struct {
suite.Suite
}
func (testSuit *AllureIDSuite) GetAllureID(testName string) string {
switch testName {
case "TestWithAllureIDFirst":
return "9001"
case "TestWithAllureIDSecond":
return "9002"
default:
return ""
}
}
func (s *AllureIDSuite) BeforeAll(t provider.T) {
// code that can fail here
}
func (s *AllureIDSuite) TestWithAllureIDFirst(t provider.T) {
// code of your test here
}
func (s *AllureIDSuite) TestWithAllureIDSecond(t provider.T) {
// code of your test here
}
func TestNewDemo(t *testing.T) {
suite.RunSuite(t, new(AllureIDSuite))
}
Example for InitializeTestsParams
method:
package suite_demo
import (
"testing"
"github.com/jackc/fake"
"github.com/ozontech/allure-go/pkg/framework/provider"
"github.com/ozontech/allure-go/pkg/framework/suite"
)
type CitiesParam struct {
allureID string
title string
value string
}
func (p CitiesParam) GetAllureID() string {
return p.allureID
}
func (p CitiesParam) GetAllureTitle() string {
return p.title
}
type ParametrizedSuite struct {
suite.Suite
ParamCities []CitiesParam
}
func (s *ParametrizedSuite) InitializeTestsParams() {
s.ParamCities = make([]CitiesParam, 2)
s.ParamCities[0] = CitiesParam{
title: "Title for city test #1",
allureID: "101",
value: fake.City(),
}
s.ParamCities[1] = CitiesParam{
title: "Title for city test #2",
allureID: "102",
value: fake.City(),
}
}
func (s *ParametrizedSuite) BeforeAll(t provider.T) {
// setup suit here
}
func (s *ParametrizedSuite) TableTestCities(t provider.T, city CitiesParam) {
t.Parallel()
t.Require().NotEmpty(city.value)
}
func TestNewParametrizedDemo(t *testing.T) {
suite.RunSuite(t, new(ParametrizedSuite))
}