Unit testing strategy for database connection get - unit-testing

There's a method in the code under test, that simply tries to get database connection, or returns error if unable to.
It, and the structs involved are defined as follows:
type DatabaseContext struct {
Context
Database DatabaseSt
}
// //GetInfo Returns the context.
// func (c *DatabaseContext) GetInfo() *Context {
// return &c.Context
// }
//GetDB Gets the database connection from the connection string.
func (c *DatabaseContext) GetDB() (*sql.DB, *errors.ErrorSt) {
var errSt *errors.ErrorSt
if c.Database.dbConnection == nil {
c.Database.dbConnection, errSt = c.openDB()
if errSt != nil {
return nil, errSt
}
c.Database.dbConnection.SetMaxOpenConns(50)
}
return c.Database.dbConnection, nil
}
The other methods, in the same file, which it may hit, are as follows:
//openDB opens the database with the connection string.
func (c *DatabaseContext) openDB() (*sql.DB, *errors.ErrorSt) {
if c.Database.DBConnectionStr == "" {
c.GetDatabase()
}
return db.OpenConnection(c.Database.DBConnectionStr, c.Database.InterpolateParams)
}
//CloseDB Closes the database.
func (c *DatabaseContext) CloseDB() {
if c.Database.dbConnection != nil {
c.Database.dbConnection.Close()
}
}
//SetDatabaseString Sets the database string into the session.
func (c *DatabaseContext) SetDatabaseString(str string) {
c.Database.DBConnectionStr = str
i := strings.Index(str, ")/") + 2
c.Database.DBName = str[i:]
c.SetDatabase()
}
//GetDatabaseString Gets the database string from the session.
func (c *DatabaseContext) GetDatabase() {
if dbIntf := c.GetFromSession("Database"); dbIntf != nil {
c.Database = dbIntf.(DatabaseSt)
}
}
//SetDatabaseString Sets the database string into the session.
func (c *DatabaseContext) SetDatabase() {
c.SetToSession("Database", c.Database)
}
Fortunately, DatabaseContext implements DatabaseContextIntf, which I want to use for testing. My instinct is to straight up mock DatabaseContext, but that won't work because it's not an interface (in Golang, you can only mock interfaces).
How would I go about testing this, without hitting a real database, which can fail beyond my control (thus creating false fails in the test)?
UPDATE My question differs from the suspected duplicate as their question is about database entries, and not connections. The flagged duplicate refers to this library as the answer, however, there is no method in it to return a "connection" that is nil, for the sake of the test. The best it has is New which creates a test double connection, and there's no way to control the state of the returned value (I need it to be nil in one test ("No Connection") and non-nil in another ("Sanity Test"))

I ended up making the package of the test the same as that of the code under test (this allows the test generator in Visual Studio Code to place the generated test right in the test file, and not get confused, as well as give me access to unexported fields, which I used), and just straight up made a fake DatabaseContext
My test case looks like this:
t.Run("SanityTest", func(t *testing.T) {
c := new(DatabaseContext)
assert.Nil(t, c.Database.dbConnection)
database, err := c.GetDB()
defer database.Close()
assert.NotNil(t, database)
if !assert.Nil(t, err) {
t.Error(err.ToString(false))
}
})

Related

Making ioutil.ReadAll(response.Body) throw error in golang

For some reason, I cannot seem to get ioutil.ReadAll(res.Body), where res is the *http.Response returned by res, err := hc.Do(redirectRequest) (for hc http.Client, redirectRequest *http.Request).
Testing strategy thus far
Any time I see hc.Do or http.Request in the SUT, my instinct is to spin up a fake server and point the appropriate application states to it. Such a server, for this test, looks like this :
badServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// some stuff
w.Write([some bad bytes])
}))
defer badServer.Close()
I don't seem to have a way to control res.Body, which is literally the only thing keeping me from 100% test completion against the func this is all in.
I tried, in the errorThrowingServer's handler func, setting r.Body to a stub io.ReadCloser that throws an error when Read() is called, but that doesn't effect res.
You can mock the body. Basically body is an io.ReadCloser interface so, you can do something like this:
import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
type mockReadCloser struct {
mock.Mock
}
func (m *mockReadCloser) Read(p []byte) (n int, err error) {
args := m.Called(p)
return args.Int(0), args.Error(1)
}
func (m *mockReadCloser) Close() error {
args := m.Called()
return args.Error(0)
}
func TestTestingSomethingWithBodyError(t *testing.T) {
mockReadCloser := mockReadCloser{}
// if Read is called, it will return error
mockReadCloser.On("Read", mock.AnythingOfType("[]uint8")).Return(0, fmt.Errorf("error reading"))
// if Close is called, it will return error
mockReadCloser.On("Close").Return(fmt.Errorf("error closing"))
request := &http.Request{
// pass the mock address
Body: &mockReadCloser,
}
expected := "what you expected"
result := YourMethod(request)
assert.Equal(t, expected, result)
mockReadCloser.AssertExpectations(t)
}
To stop reading you can use:
mockReadCloser.On("Read", mock.AnythingOfType("[]uint8")).Return(0, io.EOF).Once()
As far as I could find perusing the source files for all of the working parts, the only way to get http.Response.Body.Read() to fail is commented here:
https://golang.org/src/net/http/response.go#L53
The response body is streamed on demand as the Body field is read. If
the network connection fails or the server terminates the response,
Body.Read calls return an error.
Or there is the possibility in ioutil.ReadAll() for it to return bytes.ErrTooLarge here:
https://golang.org/src/io/ioutil/ioutil.go#L20
If the buffer overflows, we will get bytes.ErrTooLarge. Return that as
an error. Any other panic remains.

How to test if a goroutine has been called while unit testing in Golang?

suppose that we have a method like this:
func method(intr MyInterface) {
go intr.exec()
}
In unit testing method, we want to assert that inter.exec has been called once and only once; so we can mock it with another mock struct in tests, which will give us functionality to check if it has been called or not:
type mockInterface struct{
CallCount int
}
func (m *mockInterface) exec() {
m.CallCount += 1
}
And in unit tests:
func TestMethod(t *testing.T) {
var mock mockInterface{}
method(mock)
if mock.CallCount != 1 {
t.Errorf("Expected exec to be called only once but it ran %d times", mock.CallCount)
}
}
Now, the problem is that since intr.exec is being called with go keyword, we can't be sure that when we reach our assertion in tests, it has been called or not.
Possible Solution 1:
Adding a channel to arguments of intr.exec may solve this: we could wait on receiving any object from it in tests, and after receiving an object from it then we could continue to assert it being called. This channel will be completely unused in production (non-test) codes.
This will work but it adds unnecessary complexity to non-test codes, and may make large codebases incomprehensible.
Possible Solution 2:
Adding a relatively small sleep to tests before assertion may give us some assurance that the goroutine will be called before sleep is finished:
func TestMethod(t *testing.T) {
var mock mockInterface{}
method(mock)
time.sleep(100 * time.Millisecond)
if mock.CallCount != 1 {
t.Errorf("Expected exec to be called only once but it ran %d times", mock.CallCount)
}
}
This will leave non-test codes as they are now.
The problem is that it will make tests slower, and will make them flaky, since they may break in some random circumstances.
Possible Solution 3:
Creating a utility function like this:
var Go = func(function func()) {
go function()
}
And rewrite method like this:
func method(intr MyInterface) {
Go(intr.exec())
}
In tests, we could change Go to this:
var Go = func(function func()) {
function()
}
So, when we're running tests, intr.exec will be called synchronously, and we can be sure that our mock method is called before assertion.
The only problem of this solution is that it's overriding a fundamental structure of golang, which is not right thing to do.
These are solutions that I could find, but non are satisfactory as far as I can see. What is best solution?
Use a sync.WaitGroup inside the mock
You can extend mockInterface to allow it to wait for the other goroutine to finish
type mockInterface struct{
wg sync.WaitGroup // create a wait group, this will allow you to block later
CallCount int
}
func (m *mockInterface) exec() {
m.wg.Done() // record the fact that you've got a call to exec
m.CallCount += 1
}
func (m *mockInterface) currentCount() int {
m.wg.Wait() // wait for all the call to happen. This will block until wg.Done() is called.
return m.CallCount
}
In the tests you can do:
mock := &mockInterface{}
mock.wg.Add(1) // set up the fact that you want it to block until Done is called once.
method(mock)
if mock.currentCount() != 1 { // this line with block
// trimmed
}
This test won't hang forever as with sync.WaitGroup solution proposed above. It will hang for a second (in this particular example) in case when there is no call to mock.exec:
package main
import (
"testing"
"time"
)
type mockInterface struct {
closeCh chan struct{}
}
func (m *mockInterface) exec() {
close(closeCh)
}
func TestMethod(t *testing.T) {
mock := mockInterface{
closeCh: make(chan struct{}),
}
method(mock)
select {
case <-closeCh:
case <-time.After(time.Second):
t.Fatalf("expected call to mock.exec method")
}
}
This is basically what mc.Wait(time.Second) in my answer above.
first of all I would use a mock generator, i.e. github.com/gojuno/minimock
instead of writing mocks yourself:
minimock -f example.go -i MyInterface -o my_interface_mock_test.go
then your test can look like this (btw the test stub is also generated with github.com/hexdigest/gounit)
func Test_method(t *testing.T) {
type args struct {
intr MyInterface
}
tests := []struct {
name string
args func(t minimock.Tester) args
}{
{
name: "check if exec is called",
args: func(t minimock.Tester) args {
return args{
intr: NewMyInterfaceMock(t).execMock.Return(),
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mc := minimock.NewController(t)
defer mc.Wait(time.Second)
tArgs := tt.args(mc)
method(tArgs.intr)
})
}
}
In this test
defer mc.Wait(time.Second)
Waits for all mocked methods to be called.

How to mock for same input and different return values in a for loop in golang

I'm running a test with multiple parameters in a for loop using go lang testing.
I ran into a situation where same return value (and first set) is returned every time the mock is called. What I want to be able to do is change the return value for each test when the input is same i.e., same On but different Return in a loop.
I am using stretchr/testify for mocks. It looks like it will not overwrite already created mock when On is same.
func TestUpdateContactWithNewActions(t *testing.T) {
tests := []struct {
testName string
getParams func() *activities.UpdateContactWithNewActionsActivity
mockError error
}{
{"UpdateContactWithNewActions with error from contact service",
func() *activities.UpdateContactWithNewActionsActivity {
return fixtures.GetUpdateContactWithNewActionsActivity()
}, fixtures.Err},
{"UpdateContactWithNewActions valid",
func() *activities.UpdateContactWithNewActionsActivity {
return fixtures.GetUpdateContactWithNewActionsActivity()
}, nil},
}
lib.LoadWithMockClients()
for _, test := range tests {
test := test
t.Run(test.testName, func(t *testing.T) {
lib.MockCSClient.On(
"UpdateContactWithNewActions",
mock.AnythingOfType("tchannel.headerCtx"),
fixtures.UpdateContactWithNewActions).Return(test.mockError)
returnedResult, err := test.getParams().Execute(fixtures.Ctx)
if test.mockError == nil {
// some assertion
}
assert.Error(t, err)
})
}
}
I had a similar problem.
The solution was the method Once()
In your mock add an .Once() and repeat the mock with each result you need.
Something like this:
lib.Mock.On("method", arg).Return(test.mockError).Once()
lib.Mock.On("method", arg).Return(nil).Once()
Each mock result will be returned only once.
https://godoc.org/github.com/stretchr/testify/mock#Call.Once
The answer #Marcos provided works well when the result needs to be returned exactly once.
But in the scenario where each return value needs to be returned multiple (unknown) times, it won't work.
The way I solved it is by manipulating the mock.ExpectedCalls directly. In my case the mock was holding only a single method, so it was simple to just cleanup the whole ExpectedCalls slice, but in case there are multiple methods, the ExpectedCalls slice can be iterated, and update only the required call.
here is a working example for the simple case:
lib.Mock.On("method", arg).Return("1")
assert.Equal(t, lib.Mock.method(arg), "1")
assert.Equal(t, lib.Mock.method(arg), "1")
....
assert.Equal(t, lib.Mock.method(arg), "1")
lib.Mock.ExpectedCalls = nil // cleanup the previous return value
lib.Mock.On("method", arg).Return("2")
assert.Equal(t, lib.Mock.method(arg), "2")
assert.Equal(t, lib.Mock.method(arg), "2")
....
assert.Equal(t, lib.Mock.method(arg), "2")

Unit testing in golang

I'm currently looking into creating some unit tests for my service in Go, as well as other functions that build up on top of that functionality, and I'm wondering what is the best way to unit test that in Go? My code looks like:
type BBPeripheral struct {
client *http.Client
endpoint string
}
type BBQuery struct {
Name string `json:"name"`
}
type BBResponse struct {
Brand string `json:"brand"`
Model string `json:"model"`
...
}
type Peripheral struct {
Brand string
Model string
...
}
type Service interface {
Get(name string) (*Peripheral, error)
}
func NewBBPeripheral(config *peripheralConfig) (*BBPeripheral, error) {
transport, err := setTransport(config)
if err != nil {
return nil, err
}
BB := &BBPeripheral{
client: &http.Client{Transport: transport},
endpoint: config.Endpoint[0],
}
return BB, nil
}
func (this *BBPeripheral) Get(name string) (*Peripheral, error) {
data, err := json.Marshal(BBQuery{Name: name})
if err != nil {
return nil, fmt.Errorf("BBPeripheral.Get Marshal: %s", err)
}
resp, err := this.client.Post(this.endpoint, "application/json", bytes.NewBuffer(data))
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf(resp.StatusCode)
}
var BBResponse BBResponse
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
err = json.Unmarshal(body, &BBResponse)
if err != nil {
return nil, err
}
peripheral := &Peripheral{}
peripheral.Model = BBResponse.Model
if peripheral.Model == "" {
peripheral.Model = NA
}
peripheral.Brand = BBResponse.Brand
if peripheral.Brand == "" {
peripheral.Brand = NA
}
return peripheral, nil
}
Is the most efficient way of testing this code and the code that uses these functions to spin up a separate goroutine to act like the server, use http.httptest package, or something else? that's the first time that i try to write a test then i don't realy know how.
It really completely depends. Go provides pretty much all the tools you need to test your application at every single level.
Unit Tests
Design is important because there aren't many tricks to dynamically provide mock/stub objects. You can override variables for tests, but it unlocks all sorts of problems with cleanup. I would focus on IO free unit tests to check that your specific logic works.
For example, you could test BBPeripheral.Get method by making client an interface, requiring it during instantiation, and providing a stub one for the test.
func Test_BBPeripheral_Get_Success(*testing.T) {
bb := BBPeripheral{client: &StubSuccessClient, ...}
p, err := bb.Get(...)
if err != nil {
t.Fail()
}
}
Then you could create a stub error client that exercises error handling in the Get method:
func Test_BBPeripheral_Get_Success(*testing.T) {
bb := BBPeripheral{client: &StubErrClient, ...}
_, err := bb.Get(...)
if err == nil {
t.Fail()
}
}
Component/Integration Tests
These tests can help exercise that each individual unit in your package can work together in unison. Since your code talks over http, Go provides the httptest package that could be used.
To do this the test could create an httptest server with a handler registered to provide the response that this.endpoint expects. You could then exercise your code using its public interface by requesting a NewBBPeripheral, passing in this.endpoint corresponding to the Server.URL property.
This allows you to simulate your code talking to a real server.
Go Routine Tests
Go makes it so easy to write concurrent code, and makes it just as easy to test it. Testing the top level code that spawns a go routine that exercises NewBBPeripheral could look very much like the test above. In addition to starting up a test server your test will have to wait your your asynchronous code to complete. If you don't have a service wide way to cancel/shutdown/signal complete then one may be required to test it using go routines.
RaceCondition/Load Testing
Using go's built in bechmark test combined with -race flag, you can easily exercise your code, and profile it for race conditions, leveraging the tests you wrote above.
One thing to keep in mind, if the implementation of your application is still in flux, writing unit tests may cost a large amount of time. Creating a couple tests, which exercise the public interface of your code, should allow you to easily verify that your application is working, while allowing the implementation to change.

Golang - Testing with filesystem and reaching 100%

I'm trying to test one of my package to reach 100%.
However, I can't find how I can do this without being "against the system" (functions pointers, etc.).
I tried to do something similar to this, but I can't reach 100% because of "real" functions :
var fs fileSystem = osFS{}
type fileSystem interface {
Open(name string) (file, error)
Stat(name string) (os.FileInfo, error)
}
type file interface {
io.Closer
io.Reader
io.ReaderAt
io.Seeker
Stat() (os.FileInfo, error)
}
// osFS implements fileSystem using the local disk.
type osFS struct{}
func (osFS) Open(name string) (file, error) { return os.Open(name) }
func (osFS) Stat(name string) (os.FileInfo, error) { return os.Stat(name) }
(From https://talks.golang.org/2012/10things.slide#8)
Does someone would have a suggestion ? :)
Thanks !
I attempted to do same thing, just to try it. I achieved it by referencing all system file calls as interfaces and having method accept an interface. Then if no interface was provided the system method was used. I am brand new to Go, so I'm not sure if it violates best practices or not.
import "io/ioutil"
type ReadFile func (string) ([]byte, error)
type FileLoader interface {
LoadPath(path string) []byte
}
// Initializes a LocalFileSystemLoader with default ioutil.ReadFile
// as the method to read file. Optionally allows caller to provide
// their own ReadFile for testing.
func NewLocalFileSystemLoader(rffn ReadFile) *localFileSystemLoader{
var rf ReadFile = ioutil.ReadFile
if rffn != nil {
rf = rffn
}
return &localFileSystemLoader{
ReadFileFn: rf}
}
type localFileSystemLoader struct {
ReadFileFn ReadFile
}
func (lfsl *localFileSystemLoader) LoadPath(path string) []byte {
dat, err := lfsl.ReadFileFn(path)
if err != nil {
panic(err)
}
return dat
}