单元测试

The purpose of this document is to give introduction about unit tests and to help contributors in writing unit tests.

Unit Test

Read this article for a simple introduction about unit tests and benefits of unit testing. Go has its own built-in package called testing and command called go test. For more detailed information on golang’s builtin testing package read this document.

Mocks

The object which needs to be tested may have dependencies on other objects. To confine the behavior of the object under test, replacement of the other objects by mocks that simulate the behavior of the real objects is necessary. Read this article for more information on mocks.

GoMock is a mocking framework for Go programming language. Read godoc for more information about gomock.

Mock for an interface can be automatically generated using GoMocks mockgen package.

Note There is gomock package in kubeedge vendor directory without mockgen. Please use mockgen package of tagged version v1.1.1 of GoMocks github repository to install mockgen and generate mocks. Using higher version may cause errors/panics during execution of your tests.

There is gomock package in kubeedge vendor directory without mockgen. Please use mockgen package of tagged version v1.1.1 of GoMocks github repository to install mockgen and generate mocks. Using higher version may cause errors/panics during execution of your tests.

Read this article for a short tutorial of usage of gomock and mockgen.

Ginkgo

Ginkgo is one of the most popular framework for writing tests in go.

Read godoc for more information about ginkgo.

See a sample in kubeedge where go builtin package testing and gomock is used for writing unit tests.

See a sample in kubeedge where ginkgo is used for testing.

Writing UT using GoMock

Example : metamanager/dao/meta.go

After reading the code of meta.go, we can find that there are 3 interfaces of beego which are used. They are Ormer, QuerySeter and RawSeter.

We need to create fake implementations of these interfaces so that we do not rely on the original implementation of this interface and their function calls.

Following are the steps for creating fake/mock implementation of Ormer, initializing it and replacing the original with fake.

  1. Create directory mocks/beego.

  2. use mockgen to generate fake implementation of the Ormer interface

  1. mockgen -destination=mocks/beego/fake_ormer.go -package=beego github.com/astaxie/beego/orm Ormer
  • destination : where you want to create the fake implementation.
  • package : package of the created fake implementation file
  • github.com/astaxie/beego/orm : the package where interface definition is there
  • Ormer : generate mocks for this interface
  1. Initialize mocks in your test file. eg meta_test.go
  1. mockCtrl := gomock.NewController(t)
  2. defer mockCtrl.Finish()
  3. ormerMock = beego.NewMockOrmer(mockCtrl)
  1. ormermock is now a fake implementation of Ormer interface. We can make any function in ormermock return any value you want.

  2. replace the real Ormer implementation with this fake implementation. DBAccess is variable to type Ormer which we will replace with mock implemention

  1. dbm.DBAccess = ormerMock
  1. If we want Insert function of ormer interface which has return types as (int64,err) to return (1 nil), it can be done in 1 line in your test file using gomock.
  1. ormerMock.EXPECT().Insert(gomock.Any()).Return(int64(1), nil).Times(1)

Expect() : is to tell that a function of ormermock will be called.

Insert(gomock.Any()) : expect Insert to be called with any parameter.

Return(int64(1), nil) : return 1 and error nil

Times(1): expect insert to be called once and return 1 and nil only once.

So whenever insert is called, it will return 1 and nil, thus removing the dependency on external implementation.