Golang测试套件使用指南

Golang测试套件使用指南 大家好,

我已经使用 ozontech/allure-go 在内部包中编写了10个测试套件。我希望能够从 main.go 执行所有这些测试套件,而不使用命令行工具,以便完全控制测试的执行过程。请问是否有人实现过任何自定义代码来实现这个功能?

3 回复

感谢您的回复。实际上我完全同意您的观点,我也有同样的场景,需要为多个微服务编写多个测试套件。我想要的是能够整体执行这些测试套件。例如,我有 testsuite1、testsuite2、testsuite3……等等,每个测试套件包含 10 个测试用例(即测试方法)。我想运行 testsuite1 和 testsuite2 中的几个测试用例,但还没有找到任何解决方案。举个例子,如果我们使用 Cucumber,我们可以基于标签来执行特定的测试用例,这非常容易实现。

更多关于Golang测试套件使用指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好,我其实不太明白你所说的“测试套件”具体指什么,但我可以举一个例子,或许能帮到你。

我们通常将运行速度足够快的测试写在 _test.go 文件中,这是避免测试代码分离和变得混乱的最简单方法。在这种情况下,我们运行:

go test ./... -race -parallel

然而我们都知道,在开发过程中的某个阶段,如果你有 3-5 个微服务(大多数公司可能只有一个单体应用,所以你可能不需要创建端到端测试),你会开始创建涉及多个系统的更大规模的测试。有些公司称之为集成测试,有些称之为端到端测试,有些称之为系统测试,等等。它们的共同点是,一个测试用例完成所需的时间远不止眨眼那么快。

在这种情况下,在项目根目录创建一个 /tests 目录是完全没有问题的。这样,你本地运行快速测试的工作流程就变成了:运行除了 /tests 目录(也就是你那些非单元测试的大型测试套件)之外的所有测试。

go test $(go list ./… | grep -v /tests/)

你不应该试图完全控制 Go 语言本身提供的测试执行流程之外的东西。Go 已经很好地处理了缓存、竞态条件、并行化以及设置/拆卸阶段。

希望这能帮到你。在我看来,你并不需要:

强烈不建议使用那些自称是测试框架的东西,因为测试框架的功能已经内置于语言之中了。

在Golang中,你可以通过自定义测试运行器来执行测试套件,而不依赖命令行工具。以下是一个实现方案:

package main

import (
    "fmt"
    "os"
    "testing"
    "time"
    
    "github.com/ozontech/allure-go/pkg/framework/provider"
    "github.com/ozontech/allure-go/pkg/framework/suite"
)

// 定义你的测试套件接口
type TestSuite interface {
    TestAll(t *testing.T)
}

// 自定义测试运行器
type CustomTestRunner struct {
    suites []TestSuite
}

func NewCustomTestRunner() *CustomTestRunner {
    return &CustomTestRunner{
        suites: make([]TestSuite, 0),
    }
}

func (r *CustomTestRunner) AddSuite(suite TestSuite) {
    r.suites = append(r.suites, suite)
}

func (r *CustomTestRunner) RunAll() bool {
    allPassed := true
    
    for _, suite := range r.suites {
        fmt.Printf("Running test suite...\n")
        
        // 创建testing.T实例
        t := &testing.T{}
        
        // 运行测试套件
        suite.TestAll(t)
        
        if t.Failed() {
            allPassed = false
            fmt.Printf("Test suite failed\n")
        } else {
            fmt.Printf("Test suite passed\n")
        }
    }
    
    return allPassed
}

// 示例测试套件1
type MyTestSuite1 struct {
    suite.Suite
}

func (s *MyTestSuite1) TestAll(t *testing.T) {
    s.Run(t, "TestExample1", func(t provider.T) {
        t.Epic("Example")
        t.Feature("Test Suite 1")
        t.Title("Test Example 1")
        // 你的测试逻辑
        t.Require().True(true, "This should pass")
    })
    
    s.Run(t, "TestExample2", func(t provider.T) {
        t.Epic("Example")
        t.Feature("Test Suite 1")
        t.Title("Test Example 2")
        // 更多测试逻辑
    })
}

// 示例测试套件2
type MyTestSuite2 struct {
    suite.Suite
}

func (s *MyTestSuite2) TestAll(t *testing.T) {
    s.Run(t, "TestSuite2_Example1", func(t provider.T) {
        t.Epic("Example")
        t.Feature("Test Suite 2")
        t.Title("Another Test Example")
        // 测试逻辑
    })
}

func main() {
    // 创建测试运行器
    runner := NewCustomTestRunner()
    
    // 添加你的10个测试套件
    runner.AddSuite(&MyTestSuite1{})
    runner.AddSuite(&MyTestSuite2{})
    // 继续添加其他8个测试套件...
    
    // 设置Allure报告目录
    os.Setenv("ALLURE_OUTPUT_DIR", "./allure-results")
    
    fmt.Println("Starting test execution...")
    startTime := time.Now()
    
    // 运行所有测试
    allPassed := runner.RunAll()
    
    duration := time.Since(startTime)
    fmt.Printf("Test execution completed in %v\n", duration)
    
    if allPassed {
        fmt.Println("All test suites passed!")
        os.Exit(0)
    } else {
        fmt.Println("Some test suites failed!")
        os.Exit(1)
    }
}

如果你需要更细粒度的控制,这里还有一个使用testing.MainStart的替代方案:

package main

import (
    "flag"
    "fmt"
    "os"
    "testing"
)

var testSuites = []testing.InternalTest{
    {
        Name: "TestSuite1",
        F: func(t *testing.T) {
            // 调用你的第一个测试套件
            suite1 := &MyTestSuite1{}
            suite1.TestAll(t)
        },
    },
    {
        Name: "TestSuite2",
        F: func(t *testing.T) {
            // 调用你的第二个测试套件
            suite2 := &MyTestSuite2{}
            suite2.TestAll(t)
        },
    },
    // 添加更多测试套件...
}

func main() {
    flag.Parse()
    
    // 使用testing包的内置运行器
    m := testing.MainStart(testDeps{}, testSuites, nil, nil, nil)
    
    exitCode := m.Run()
    
    fmt.Printf("Exit code: %d\n", exitCode)
    os.Exit(exitCode)
}

// 实现testing.testDeps接口
type testDeps struct{}

func (d testDeps) MatchString(pat, str string) (bool, error) {
    return false, nil
}

func (d testDeps) StartCPUProfile(interface{}) error {
    return nil
}

func (d testDeps) StopCPUProfile() {}

func (d testDeps) WriteProfileTo(string, interface{}, interface{}) error {
    return nil
}

func (d testDeps) ImportPath() string {
    return ""
}

func (d testDeps) StartTestLog(interface{}) {}

func (d testDeps) StopTestLog() error {
    return nil
}

对于allure-go的集成,确保你的测试套件正确实现了测试方法:

// 在你的测试套件包中
package mytests

import (
    "testing"
    
    "github.com/ozontech/allure-go/pkg/framework/provider"
    "github.com/ozontech/allure-go/pkg/framework/suite"
)

type YourTestSuite struct {
    suite.Suite
}

func (s *YourTestSuite) TestAll(t *testing.T) {
    // 使用Allure的Run方法执行测试
    s.Run(t, "YourTestName", func(t provider.T) {
        t.Epic("YourEpic")
        t.Feature("YourFeature")
        t.Title("Test Title")
        
        // 测试步骤
        t.WithNewStep("Step 1", func(ctx provider.StepCtx) {
            // 测试逻辑
            t.Require().Equal(1, 1, "Values should be equal")
        })
    })
}

这种方法让你完全控制测试执行流程,可以集成到CI/CD流水线中,或者与其他系统进行集成。

回到顶部