首页 > 其他分享 >Go - Creating Subtests to Have Finer Control Over Groups of Test Cases

Go - Creating Subtests to Have Finer Control Over Groups of Test Cases

时间:2023-10-18 16:36:03浏览次数:35  
标签:Control Creating TestAddWithSubTest Over testCase result func PASS test

Problem: You want to create subtests within a test function to have finer control over test cases.


Solution: Use the t.Run function to create subtests within a test function. Subtests extend the flexibility of test functions to another level down.

 

When using table - driven tests, you often want to run specific tests or have finer - grained control over the test cases. However, table - driven tests are really driven through data so there is no way you can control it. For example, in the previous test function using table - driven tests you can only test the results for nonequality for every single test case:

func TestAddWithTables(t *testing.T) {
    testCases := []struct {
        a int
        b int
        result int
    }{
        {1, 2, 3},
        {12, 30, 42},
        {100, -1, 99},
    }

    for _, testCase := range testCases {
        result := Add(testCase.a, testCase.b)
        if result != testCase.result {
            t.Errorf("Adding  %d  and  %d  doesn't  produce  %d,  instead it  produces  %d",
                testCase.a, testCase.b, testCase.result, result)
        }
    }
}

Go 1.7 added a new feature to allow subtests within a test function, using the t.Run function. This is how you can turn your table - driven test function into one that uses subtests:

func TestAddWithSubTest(t *testing.T) {
    testCases := []struct {
        name string
        a int
        b int
        result int
    }{
        {"OneDigit", 1, 2, 3},
        {"TwoDigits", 12, 30, 42},
        {"ThreeDigits", 100, -1, 99},
    }

    for _, testCase := range testCases {
        t.Run(testCase.name, func(t *testing.T) {
            result := Add(testCase.a, testCase.b)
            if result != testCase.result {
                t.Errorf("Adding  %d  and  %d  doesn't  produce  %d,  instead  it  produces  %d",
                    testCase.a, testCase.b, testCase.result, result)
            }
        })
    }
}

As you can see, you added a test name to each of the test cases. Then within the for loop, you call t.Run , passing it the test case name and also calling an anonymous function that has the same form as a normal test function. This is the subtest from which you can run each test case. 

Run the test now and see what happens:
% go test - v - run TestAddWithSubTest

=== RUN TestAddWithSubTest

=== RUN TestAddWithSubTest/OneDigit

=== RUN TestAddWithSubTest/TwoDigits

=== RUN TestAddWithSubTest/ThreeDigits

- - - PASS: TestAddWithSubTest (0.00s)

- - - PASS: TestAddWithSubTest/OneDigit (0.00s)

- - - PASS: TestAddWithSubTest/TwoDigits (0.00s)

- - - PASS: TestAddWithSubTest/ThreeDigits (0.00s)

PASS

ok github.com/sausheong/gocookbook/ch18_testing 0.607s

As you can see, each subtest is named and run separately under the umbrella test function. You could, in fact, pick and choose the subtest you want to run:
% go test - v - run TestAddWithSubTest/TwoDigits

=== RUN TestAddWithSubTest

=== RUN TestAddWithSubTest/TwoDigits

- - - PASS: TestAddWithSubTest (0.00s)

- - - PASS: TestAddWithSubTest/TwoDigits (0.00s)

PASS

ok github.com/sausheong/gocookbook/ch18_testing 0.193s

In the preceding example, you didn’t do anything more than what you did before, but you could have done more to customize the setup and teardown. For example:

func TestAddWithCustomSubTest(t *testing.T) {
    testCases := []struct {
        name string
        a int
        b int
        result int
        setup func()
        teardown func()
    }{
        {"OneDigit", 1, 2, 3,
            func() { fmt.Println("setup  one") },
            func() { fmt.Println("teardown  one") }},
        {"TwoDigits", 12, 30, 42,
            func() { fmt.Println("setup  two") },
            func() { fmt.Println("teardown  two") }},
        {"ThreeDigits", 100, -1, 99,
            func() { fmt.Println("setup  three") },
            func() { fmt.Println("teardown  three") }},
    }
    for _, testCase := range testCases {
        t.Run(testCase.name, func(t *testing.T) {
            testCase.setup()
            defer testCase.teardown()
            result := Add(testCase.a, testCase.b)
            if result != testCase.result {
                t.Errorf("Adding  %d  and  %d  doesn't  produce  %d, instead  it  produces  %d",
                    testCase.a, testCase.b, testCase.result, result)
            } else {
                fmt.Println(testCase.name, "ok.")
            }
        })
    }
}

If you run it now, you can see that each subtest has its own setup and teardown functions that are called separately:
% go test - v - run TestAddWithCustomSubTest

=== RUN TestAddWithCustomSubTest

=== RUN TestAddWithCustomSubTest/OneDigit

setup one

OneDigit ok.

teardown one

=== RUN TestAddWithCustomSubTest/TwoDigits

setup two

TwoDigits ok.

teardown two

=== RUN TestAddWithCustomSubTest/ThreeDigits

setup three

ThreeDigits ok.

teardown three

- - - PASS: TestAddWithCustomSubTest (0.00s)

- - - PASS: TestAddWithCustomSubTest/OneDigit (0.00s)

- - - PASS: TestAddWithCustomSubTest/TwoDigits (0.00s)

- - - PASS: TestAddWithCustomSubTest/ThreeDigits (0.00s)

PASS

ok github.com/sausheong/gocookbook/ch18_testing 0.278s

In the previous examples, you used subtests on a table - driven test. However, it doesn’t need to be table - driven. Subtests can be used simply to group different tests under a single test function. For example, in the following case, you want to group the tests on the flip function under a single test function:

func TestFlipWithSubTest(t *testing.T) {
    grid := load("monalisa.png") //  setup  for  all  flip  tests

    t.Run("CheckPixels", func(t *testing.T) {
        p1 := grid[0][0]
        flip(grid)
        defer flip(grid) //  teardown  for  check  pixel  to  unflip  the  grid
        p2 := grid[0][479]
        if p1 != p2 {
            t.Fatal("Pixels  not  flipped")
        }
    })

    t.Run("CheckDimensions", func(t *testing.T) {
        flip(grid)
        save("flipped.png", grid)
        //  teardown  for  check  dimensions  to  remove  the  file
        defer os.Remove("flipped.png")
        g := load("flipped.png")
        if len(g) != 321 || len(g[0]) != 480 {
            t.Error("Grid  is  wrong  size", "width:", len(g),
                "length:", len(g[0]))
        }
    })
}

In this case, you have a single setup for all the flip tests but different teardowns for individual test cases. Each test case can be run as a different test function, but grouping them together allows you to have a one - time setup for test fixtures and also to run multiple subtests under a single umbrella:
% go test - v - run TestFlipWithSubTest

=== RUN TestFlipWithSubTest

=== RUN TestFlipWithSubTest/CheckPixels

=== RUN TestFlipWithSubTest/CheckDimensions

- - - PASS: TestFlipWithSubTest (0.07s)

- - - PASS: TestFlipWithSubTest/CheckPixels (0.00s)

- - - PASS: TestFlipWithSubTest/CheckDimensions (0.05s)

PASS

ok github.com/sausheong/gocookbook/ch18_testing 0.269s

 

标签:Control,Creating,TestAddWithSubTest,Over,testCase,result,func,PASS,test
From: https://www.cnblogs.com/zhangzhihui/p/17772678.html

相关文章

  • Go - Creating a JSON Web Service API
    Problem: YouwanttocreateasimplewebserviceAPIthatreturnsJSON.Solution: Usethenet/httppackagetocreateawebserviceAPIandtheencoding/jsonpackagetoencodedatatobesentbackasJSON. You’llcreateawebserviceAPIthatreturnsa......
  • Secure Code Warrior C# Basic OWASP Web Top 10 2017 5: Broken Access Control, 6:
    Learntheropesorhoneyourskillsinsecureprogramminghere.Thesechallengeswillgiveyouanunderstandingof5:BrokenAccessControl,6:SecurityMisconfigurationand7:XSSvulnerabilities5:BrokenAccessControl, 6:SecurityMisconfiguration ......
  • 一图看懂CodeArts Governance 三大特性,带你玩转开源治理服务
    华为云开源治理服务CodeArtsGovernance是针对软件研发提供的一站式开源软件治理服务,凝聚华为在开源治理上的优秀实践经验,提供开源软件元数据及软件成分分析、恶意代码检测等能力,从合法合规、网络安全、供应安全等维度消减开源软件使用风险,助力企业更加安全、高效地使用开源软件。......
  • jenkins定时清理overlay2
    新建一个自由风格的任务 填写描述 七天清理一次,可以更改别的时间 清理命令 ......
  • WPF控件ItemsControl、ListBox、ListView、DataGrid、TreeView、TabControl用法及区别
    1.ItemsControltemsControl是WPF中最基本的控件之一,用于显示一个数据项集合。它允许按照自定义方式呈现任何类型的对象,可以在其中使用不同的布局和面板来展示数据。ItemsControl非常灵活,可以满足各种需求。以下是一个简单的ItemsControl的XAML示例,它使用StackPanel作为布局容器,......
  • 循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(9) -- 实现系
    在WPF应用端开发,它的界面类似于Winform端,因此我们也需要对系统的菜单进行动态配置,这样才能把系统的功能弹性发挥到极致,通过动态菜单的配置方式,我们可以很容易的为系统新增所需的功能,通过权限分配的方式,可以更有效的管理系统的菜单分配到不同的角色用户,本篇随笔介绍在WPF应用端中实......
  • CF1680F Lenient Vertex Cover 题解
    CF1680FLenientVertexCover题解这道题和「JOISC2014Day3」电压非常类似,或者说就是一道题。题意就是给你一个图,问能否对所有点黑白染色,允许最多一条边的两个顶点都染成黑色。黑白染色后其实就是一个二分图,那如果有一条边的两个顶点染成黑色,就是说去掉该边后,剩下的图为二分......
  • Go - Creating a UDP Client
    Problem: YouwanttocreateaUDPclienttosenddatatoaUDPserver.Solution: UsetheDialfunctioninthenetpackagetoconnecttoaUDPserver.ThenusetheWritemethodofthenet.UDPConninterfacetowritedatatotheconnection. CreatingaUDP......
  • Go - Creating a UDP Server
    Problem: YouwanttocreateaUDPservertoreceivedatafromaUDPclient.Solution: UsetheListenPacketfunctioninthenetpackagetolistenforincomingpackets.ThenusetheReadFrommethodofthePacketConninterfacetoreaddatafromtheconnecti......
  • EC20 scancontrol
    EC20scancontrol ============Lockingofband28https://forums.quectel.com/t/locking-of-band-28/10101/14 Pleasewhichcommandtolockband28.Alsowhichcommandtoreverttodefaultsetup. hi,Kizsmart_Ventures:Ifyouhav......