- Install goconvey by running
go get github.com/smartystreets/goconvey
The following make targets can be used to run all tests in this in project:
make test
will run all unit tests in addition togofmt
,govet
,golint
, andgosec
make test-all
will runmake test
in addition to all integration tests
- Test name should be
Test
plus the method name tested (eg:TestIsBoolExtensionEnabled
) - Inputs parameters and expected outputs should be identifiable by their names
- Inputs should have names beginning with
input
(eg:inputStatusCode
) - Expected outputs should have names beginning with
expected
(eg:expectedResult
)
- Inputs should have names beginning with
- Organize tests using
Given
,When
, andThen
Convey statements to make the intention of the test clear:Given
: The context/setup for the behavior being testedWhen
: The behavior being testedThen
: Assert on the expected outputs
- Assertions should use the relevant So comparison
- Tests with multiple test cases should follow a combined table driven test and Convey format. See the multiple tests cases example.
No need to follow the table driven test format for one test case.
func TestNewSpecV2Resource(t *testing.T) {
Convey("Given a root path /users, a root path item, schema definitions", t, func() {
inputPath := "/users"
inputRootPathItem := spec.PathItem{
PathItemProps: spec.PathItemProps{
Post: &spec.Operation{},
},
}
inputSchemaDefinitions := map[string]spec.Schema{}
Convey("When the newSpecV2Resource method is called", func() {
r, err := newSpecV2Resource(inputPath, spec.Schema{}, inputRootPathItem, spec.PathItem{}, inputSchemaDefinitions, map[string]spec.PathItem{})
Convey("Then the resource returned should have name `users` and there should be no error", func() {
So(r.GetResourceName(), ShouldEqual, "users")
So(err, ShouldBeNil)
})
})
})
}
Follow the table driven test format with a Convey statement wrapper around the test cases loop.
The When
Convey statement should include a reference to the test case name. This allows you to differentiate between test cases when running tests in verbose mode (go test -v
) since each test case name will be printed.
func TestIsBoolExtensionEnabled(t *testing.T) {
testCases := []struct {
name string
inputExtensions spec.Extensions
inputExtension string
expectedResult bool
expectedError error
}{
{name: "nil extensions", inputExtensions: nil, inputExtension: "", expectedResult: false},
{name: "empty extensions", inputExtensions: spec.Extensions{}, inputExtension: "", expectedResult: false},
{name: "populated extensions but empty extension", inputExtensions: spec.Extensions{"some-extension": true}, inputExtension: "", expectedResult: false},
{name: "populated extensions and matching bool extension with value true", inputExtensions: spec.Extensions{"some-extension": true}, inputExtension: "some-extension", expectedResult: true},
{name: "populated extensions and matching bool extension with value false", inputExtensions: spec.Extensions{"some-extension": false}, inputExtension: "some-extension", expectedResult: false},
{name: "populated extensions and matching non bool extension", inputExtensions: spec.Extensions{"some-extension": "some value"}, inputExtension: "some-extension", expectedResult: false},
}
Convey("Given a SpecV2Resource", t, func() {
r := &SpecV2Resource{}
for _, tc := range testCases {
Convey(fmt.Sprintf("When isBoolExtensionEnabled method is called: %s", tc.name), func() {
isEnabled, err := r.testMethod(tc.inputExtensions, tc.inputExtension)
Convey("Then the result returned should be the expected one", func() {
So(err, ShouldResemble, tc.expectedError)
So(isEnabled, ShouldEqual, tc.expectedResult)
})
})
}
})
}
In some cases, the method under tests may not need any special set up. The following example can be used as an example on how to write such tests:
func TestExpandPath(t *testing.T) {
testCases := []struct {
name string
inputPath string
expectedResult string
expectedError error
}{
{name: "input example 1", inputPath: "input1", expectedResult: "result1", expectedError: nil,},
{name: "input example 2", inputPath: "input2", expectedResult: "result2", expectedError: nil,},
}
for _, tc := range testCases {
Convey(fmt.Sprintf("When testMethod method is called: %s", tc.name), t, func() {
returnedPath, err := testMethod(tc.inputPath)
Convey("Then the result returned should be the expected one", func() {
So(err, ShouldResemble, tc.expectedError)
So(returnedPath, ShouldEqual, tc.expectedResult)
})
})
}
}