Go single test series 6 - use of goconvey

This is the fifth in a series of tutorials on Go language unit testing from zero to slip. It introduces how to use gocovey to better write unit tests and make the unit test results more intuitive and visual.

In the previous "Go single test series 5 - Monkey piling test", we introduced how to use monkey to pile functions and methods in unit test.

In this article, we will introduce a personalized unit testing tool - goconvey.

The sample code of Go single test from zero to slide series has been uploaded to Github, click 👉🏻 https://github.com/go-quiz/golang-unit-test-demo View the complete source code.

Introduction to gocovey

GoConvey It is a very useful Go testing framework. It is directly integrated with go test, provides many rich assertion functions, can output readable color test results at the terminal, and also supports fully automatic Web UI.

install

 go get github.com/smartystreets/goconvey

Use example

We used gocovey to write unit tests for the Split function in the initial basic example. The Split function is as follows:

// split.go

func Split(s, sep string) (result []string) {
	result = make([]string, 0, strings.Count(s, sep)+1)
	i := strings.Index(s, sep)
	for i > -1 {
		result = append(result, s[:i])
		s = s[i+len(sep):]
		i = strings.Index(s, sep)
	}
	result = append(result, s)
	return
}

The contents of unit test documents are as follows:

// split_test.go


import (
	"testing"

	c "github.com/smartystreets/goconvey/convey"  // Alias import
)

func TestSplit(t *testing.T) {
	c.Convey("Basic use case", t, func() {
		var (
			s      = "a:b:c"
			sep    = ":"
			expect = []string{"a", "b", "c"}
		)
		got := Split(s, sep)
		c.So(got, c.ShouldResemble, expect) // Assert
	})

	c.Convey("Use case without separator", t, func() {
		var (
			s      = "a:b:c"
			sep    = "|"
			expect = []string{"a:b:c"}
		)
		got := Split(s, sep)
		c.So(got, c.ShouldResemble, expect) // Assert
	})
}

The command line executes the unit test, and the color results with very good readability will be output at the terminal.

Gocovey also supports nested calls as needed in unit tests, such as:

func TestSplit(t *testing.T) {
	// ...

	// You only need to pass in t when the conversation call at the top level
	c.Convey("Separator at beginning or end of use case", t, func() {
		tt := []struct {
			name   string
			s      string
			sep    string
			expect []string
		}{
			{"Separator at beginning", "*1*2*3", "*", []string{"", "1", "2", "3"}},
			{"Separator at end", "1+2+3+", "+", []string{"1", "2", "3", ""}},
		}
		for _, tc := range tt {
			c.Convey(tc.name, func() { // Nested call conference
				got := Split(tc.s, tc.sep)
				c.So(got, c.ShouldResemble, tc.expect)
			})
		}
	})
}

In this way, the final test results will also be displayed at the hierarchical level.

assert methods

Gocovey provides us with many kinds of assertion methods to use in the So() function.

General equivalence class
So(thing1, ShouldEqual, thing2)
So(thing1, ShouldNotEqual, thing2)
So(thing1, ShouldResemble, thing2)		// Used for equality of arrays, slices, map s, and structs
So(thing1, ShouldNotResemble, thing2)
So(thing1, ShouldPointTo, thing2)
So(thing1, ShouldNotPointTo, thing2)
So(thing1, ShouldBeNil)
So(thing1, ShouldNotBeNil)
So(thing1, ShouldBeTrue)
So(thing1, ShouldBeFalse)
So(thing1, ShouldBeZeroValue)
Digital quantity comparison class
So(1, ShouldBeGreaterThan, 0)
So(1, ShouldBeGreaterThanOrEqualTo, 0)
So(1, ShouldBeLessThan, 2)
So(1, ShouldBeLessThanOrEqualTo, 2)
So(1.1, ShouldBeBetween, .8, 1.2)
So(1.1, ShouldNotBeBetween, 2, 3)
So(1.1, ShouldBeBetweenOrEqual, .9, 1.1)
So(1.1, ShouldNotBeBetweenOrEqual, 1000, 2000)
So(1.0, ShouldAlmostEqual, 0.99999999, .0001)   // tolerance is optional; default 0.0000000001
So(1.0, ShouldNotAlmostEqual, 0.9, .0001)
Include class
So([]int{2, 4, 6}, ShouldContain, 4)
So([]int{2, 4, 6}, ShouldNotContain, 5)
So(4, ShouldBeIn, ...[]int{2, 4, 6})
So(4, ShouldNotBeIn, ...[]int{1, 3, 5})
So([]int{}, ShouldBeEmpty)
So([]int{1}, ShouldNotBeEmpty)
So(map[string]string{"a": "b"}, ShouldContainKey, "a")
So(map[string]string{"a": "b"}, ShouldNotContainKey, "b")
So(map[string]string{"a": "b"}, ShouldNotBeEmpty)
So(map[string]string{}, ShouldBeEmpty)
So(map[string]string{"a": "b"}, ShouldHaveLength, 1) // supports map, slice, chan, and string
String class
So("asdf", ShouldStartWith, "as")
So("asdf", ShouldNotStartWith, "df")
So("asdf", ShouldEndWith, "df")
So("asdf", ShouldNotEndWith, "df")
So("asdf", ShouldContainSubstring, "wait a jiff")		// optional 'expected occurences' arguments?
So("asdf", ShouldNotContainSubstring, "er")
So("adsf", ShouldBeBlank)
So("asdf", ShouldNotBeBlank)
panic class
So(func(), ShouldPanic)
So(func(), ShouldNotPanic)
So(func(), ShouldPanicWith, "")		// or errors.New("something")
So(func(), ShouldNotPanicWith, "")	// or errors.New("something")
Type check class
So(1, ShouldHaveSameTypeAs, 0)
So(1, ShouldNotHaveSameTypeAs, "asdf")
Time and interval classes
So(time.Now(), ShouldHappenBefore, time.Now())
So(time.Now(), ShouldHappenOnOrBefore, time.Now())
So(time.Now(), ShouldHappenAfter, time.Now())
So(time.Now(), ShouldHappenOnOrAfter, time.Now())
So(time.Now(), ShouldHappenBetween, time.Now(), time.Now())
So(time.Now(), ShouldHappenOnOrBetween, time.Now(), time.Now())
So(time.Now(), ShouldNotHappenOnOrBetween, time.Now(), time.Now())
So(time.Now(), ShouldHappenWithin, duration, time.Now())
So(time.Now(), ShouldNotHappenWithin, duration, time.Now())
Custom assertion method

If none of the assertion methods listed above can meet your needs, you can also customize an assertion method according to the following format.

Note: the content in < > is what you need to replace according to your actual needs.

func should<do-something>(actual interface{}, expected ...interface{}) string {
    if <some-important-condition-is-met(actual, expected)> {
        return ""   // Returns an empty string indicating that the assertion passed
    }
    return "<Some descriptive messages specify why the assertion failed...>"
}

WebUI

Gocovey provides a fully automatic Web UI. You only need to execute the following commands in the project directory.

goconvey

By default, a WebUI interface will be provided on the 8080 port of the machine to clearly show the unit test data of the current project.

summary

Through a complete unit test example, this paper introduces how to use gocovey tool to write test cases, manage test cases and assert test results. At the same time, it also introduces the rich and diverse output forms of test results of gocovey.

In the next article, which will be the last in this series, we will focus on how to write testable code.

 

Keywords: Go

Added by kryppienation on Sat, 29 Jan 2022 03:21:26 +0200