Skip to content

Latest commit

 

History

History
143 lines (111 loc) · 8.62 KB

File metadata and controls

143 lines (111 loc) · 8.62 KB

Migration guide

How to move existing tests to Testo - from testify or from allure-go.

Both migrations can be done incrementally: Testo tests are regular go test tests, so old and new suites run side by side in the same package with no interference. Migrate one suite at a time.

Migrating from Testify

Suites and hooks

Testify Testo
suite.Suite (embedded) testo.Suite[T] (embedded)
suite.Run(t, new(S)) testo.RunSuite(t, new(S))
func (s *S) SetupSuite() func (*S) BeforeAll(t T)
func (s *S) SetupTest() func (*S) BeforeEach(t T)
func (s *S) TearDownTest() func (*S) AfterEach(t T)
func (s *S) TearDownSuite() func (*S) AfterAll(t T)
func (s *S) TestFoo() func (*S) TestFoo(t T)
s.T() t (passed to every method)
s.Run(name, func()) testo.Run(t, name, func(t T))
func (s *S) BeforeTest(suiteName, testName string) BeforeEach(t T) + t.Name() (same for AfterTest)
SetupSubTest() / TearDownSubTest() BeforeEachSub/AfterEachSub plugin hooks (no suite-level equivalent)
go test -run TestSuite -testify.m TestFoo go test -run TestSuite -testo.m TestFoo

Note

t.Name() returns the full path (Test/MySuite/TestFoo), not the bare method name - compare with strings.HasSuffix, not ==. And if a suite uses both SetupTest and BeforeTest, merge them into one BeforeEach (testify runs SetupTest first; keep that order).

One caveat: testify method filters like -run 'TestMySuite/TestFoo' stop matching after migration, because Testo inserts a hidden testo! level into test names. Update CI filters to use -testo.m. See how to run and skip specific tests.

T here is your own type built on *testo.T - in the simplest case just an alias:

type T = *testo.T

See the tutorial for defining a T with plugins.

Assertions

testify's require and assert packages work with Testo unchanged, because testo.T implements testing.TB:

// testify/suite:
func (s *MySuite) TestFoo() {
    s.Require().NoError(err)
    s.Equal(want, got) // shorthand from the embedded suite.Suite
}

// Testo:
func (*MySuite) TestFoo(t T) {
    require.NoError(t, err)
    assert.Equal(t, want, got)
}

Testo only replaces testify's suite package. Keep using require and assert.

Suite state

Testify suites keep per-test state in suite struct fields. A Testo suite is also a single instance, so shared fields still work. But writes to shared fields from parallel tests are a data race - run migrated packages with -race before enabling t.Parallel(). For per-test state, prefer fixtures (methods on your T) or plugin fields. Both get a fresh instance for each test and are safe with parallel tests.

Parallelism

Testify's suite does not support t.Parallel() (issue #934). With Testo, call t.Parallel() in any test - hooks handle parallel tests correctly. See how to write parallel tests.

Migrating from Allure-Go

Testo replaces Allure-Go at Ozon. Reporting is now a separate plugin, not part of the framework. So you need two pieces: github.com/ozontech/testo and the testo-allure plugin.

import (
    "github.com/ozontech/testo"

    allure "github.com/ozontech/testo-allure"
)

type T struct {
    *testo.T
    *allure.PluginAllure
}

Concept mapping

allure-go Testo (+ testo-allure)
suite.Suite testo.Suite[T]
suite.RunSuite(t, new(S)) (or runner.NewSuiteRunner(...)) testo.RunSuite(t, new(S))
BeforeAll(t provider.T) BeforeAll(t T) (same for the other hooks)
func (s *S) TestFoo(t provider.T) func (*S) TestFoo(t T)
t.WithNewStep("name", func(ctx provider.StepCtx) {...}) allure.Step(t, "name", func(t T) {...})
t.NewStep("name") allure.Step(t, "name", func(T) {})
t.Title, t.Epic, t.Feature, t.Story, t.Tags, t.Severity, t.Owner, t.ID same-named methods added to T by PluginAllure
t.Link(...), t.TmsLink(...), t.TmsLinks(...) t.Links(...) with the allure.NewLink/allure.TMS/allure.Issue constructors
t.WithNewAttachment(name, mimeType, content), t.WithAttachments(...) t.Attach(name, allure.Bytes(...)) or t.Attach(name, allure.File(...))
table tests (ParametrizedSuite / TableTestXxx methods) single struct-typed parameter, see table tests
t.XSkip() t.XFail() via the xfail plugin from testo-toppings (semantics differ slightly - check its README)

Steps

Steps are sub-tests. Two ways to create them:

  • testo.Run(t, "name", func(t T) {...}) - the framework primitive. t.Fatal inside stops only the sub-test, not the outer test.
  • allure.Step(t, "name", func(t T) {...}) - the testo-allure helper. Same as testo.Run, but a fatal failure propagates to the parent test and stops it - matching WithNewStep semantics from allure-go.

Use allure.Step instead of WithNewStep. Nested steps (sCtx.WithNewStep, sCtx.NewStep) become nested allure.Step(t, ...) calls.

Assertions sCtx.Assert() and sCtx.Require() become t.Assert() & t.Require(), which PluginAllure adds to T.

For the full testo-allure API (output directory, labels, attachments and so on), see the testo-allure documentation.