In the last video, we created our Rocket service which will manage our inventory of rockets by making calls to the backend store. At this point though, we want to start adding some basic unit tests to our application and test that the logic in our Rocket service works as intended.
Installing Mockgen
We’ll need to install two dependencies prior to us being able to generate the mocks we need in order to run our test.
$ go get github.com/golang/mock/gomock
$ go get github.com/golang/mock/mockgen
Mocking our Database
We haven’t yet implemented our db
package yet, so we don’t have a concrete implementation that will allow us to add and remove rockets from our backend database.
We do however, have an interface that we can use to generate mocked code which can then be used within our unit tests for our rocket
service.
We’ll want to use the mockgen
library in order to generate our mocked code. Let’s add the compiler directives at the start of our rocket.go
file that mockgen
will then be able to generate our mocks from:
//go:generate mockgen -destination=rocket_mocks_test.go -package=rocket github.com/TutorialEdge/go-grpc-services-course/internal/rocket Store
package rocket
import "context"
// Rocket - should contain the definition of our
// rocket
type Rocket struct {
ID string
Name string
Type string
Flights int
}
// the rest of our rocket.go implementation
With this in place, let’s try and generate our mocks now using the following command:
$ go generate ./...
This should execute successfully and we should be left with a new file called rocket_mocks_test.go
within our internal/rocket
directory.
Implementing our Unit Tests
Now that our mocks are ready to go, let’s set about creating some unit tests to validate our service implementation.
Create a new file called rocket_test.go
under internal/rocket
and let’s add some tests:
package rocket
import (
"context"
"testing"
gomock "github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)
func TestRocketService(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
t.Run("tests get rocket by id", func(t *testing.T) {
rocketStoreMock := NewMockStore(mockCtrl)
id := "UUID-1"
rocketStoreMock.
EXPECT().
GetRocketByID(id).
Return(Rocket{
ID: id,
}, nil)
rocketService := New(rocketStoreMock)
rkt, err := rocketService.
GetRocketByID(
context.Background(),
id,
)
assert.NoError(t, err)
assert.Equal(t, "UUID-1", rkt.ID)
})
t.Run("tests insert rocket", func(t *testing.T) {
rocketStoreMock := NewMockStore(mockCtrl)
id := "UUID-1"
rocketStoreMock.
EXPECT().
InsertRocket(Rocket{
ID: id,
}).
Return(Rocket{
ID: id,
}, nil)
rocketService := New(rocketStoreMock)
rkt, err := rocketService.
InsertRocket(
context.Background(),
Rocket{
ID: id,
},
)
assert.NoError(t, err)
assert.Equal(t, "UUID-1", rkt.ID)
})
t.Run("tests delete rocket", func(t *testing.T) {
rocketStoreMock := NewMockStore(mockCtrl)
id := "UUID-1"
rocketStoreMock.
EXPECT().
DeleteRocket(id).
Return(nil)
rocketService := New(rocketStoreMock)
err := rocketService.
DeleteRocket(
id,
)
assert.NoError(t, err)
})
}
With these tests in place, let’s try running them and see if our tests are passing:
$ go test ./... -v
? github.com/TutorialEdge/go-grpc-services-course/cmd/server [no test files]
=== RUN TestRocketService
=== RUN TestRocketService/tests_get_rocket_by_id
=== RUN TestRocketService/tests_insert_rocket
=== RUN TestRocketService/tests_delete_rocket
--- PASS: TestRocketService (0.00s)
--- PASS: TestRocketService/tests_get_rocket_by_id (0.00s)
--- PASS: TestRocketService/tests_insert_rocket (0.00s)
--- PASS: TestRocketService/tests_delete_rocket (0.00s)
PASS
ok github.com/TutorialEdge/go-grpc-services-course/internal/rocket 0.130s
Conclusion
Perfect, we’ve been able to build a little bit more confidence in our system by adding a few tests around our core business logic.
Adding tests often and early is critical to building solid, reliable systems that won’t fail you in production and whilst this is a course around gRPC, it is vital we uphold these standards at all times!