gogo-testinggocql

How to write unit test case for gocql


package main 

import "github.com/gocql/gocql"


var CassandraSession *gocql.Session

func getSelectQuery(ClientId, UID, UUID, tableName, selectColumns string, sess *gocql.Session) (*gocql.Query, error) {
    
    selectQuery := fmt.Sprintf(qTamplate, selectColumns, tableName)
        query := sess.Query(selectQuery, ClientId, UID).Consistency(gocql.One)
    err = query.Exec()
    if err != nil {
        return query, err
    }
    
    return query, nil
}


func GetSeglistFromCassandra(reqId string, dataMap MapUserRequest) (models.IsInSegmentResponse, error) {
    resutMap := models.IsInSegmentResponse{}
    tableName := fmt.Sprintf("getter_%v", cid[len(cid) - 1])
    cqlsh := CassandraSession
    selectColums := "cohortid, is_deleted, cohortype"
    selectQueryObj, err := getSelectQuery(dataMap.ClientId, dataMap.UID, dataMap.UUID, tableName, selectColums, cqlsh)
    if err != nil {
        return resutMap, err
    }
    var id, deleted, cohortType int
    iter := selectQueryObj.Iter()
    for iter.Scan(&id, &deleted, &cohortType) {
        if deleted == 0 {
            if cohortType == 3 || cohortType == 2 {
                resutMap.ListId = append(resutMap.ListId, id)
            } else {
                resutMap.SegIds = append(resutMap.SegIds, id)
            }
        }
    }
    if err := iter.Close(); err != nil {
       return resultMap, err
    }
    return resutMap, nil
}

How to write unit test case in golang for this function GetSeglistFromCassandra which is using gocql without connecting to actual database if we can mock the gocql session creation, queries and iteration with example?


Solution

  • Summary

    In my opinion, there is no way to write a unit test except for making an interface for gocql and its implementations. its implementations will be a wrapper struct for gocql and a mock for test.

    The reason why we should use an interface and its wrapper struct is gocql provides their concrete types such as gocql.Session.

    My suggestion

    I suggest my approach to you. For that, I make your codes more simpler to handle.

    simple.go

    package mockgocql
    
    import (
        "fmt"
    
        "github.com/gocql/gocql"
    )
    
    // SessionInterface allows gomock mock of gocql.Session
    type SessionInterface interface {
        Query(stmt string, values ...interface{}) QueryInterface
    }
    
    // QueryInterface allows gomock mock of gocql.Query
    type QueryInterface interface {
        Exec() error
        Consistency(c gocql.Consistency) QueryInterface
    }
    
    func simpleGetSelectQuery(ClientId, UID, UUID, tableName, selectColumns string, sessionInterface SessionInterface) (QueryInterface, error) {
        selectQuery := fmt.Sprintf(selectColumns, tableName)
        query := sessionInterface.Query(selectQuery, ClientId, UUID, UID).Consistency(gocql.One)
    
        err := query.Exec()
        if err != nil {
            return query, err
        }
    
        return query, nil
    }
    
    func simpleGetSeglistFromCassandra(cqlsh SessionInterface, reqId string) error {
        selectColums := "cohortid, is_deleted, cohortype"
        _, err := simpleGetSelectQuery("ClientId", "Uid", "Uuid", "tableName", selectColums, cqlsh)
        if err != nil {
            return err
        }
        return err
    }
    

    impl.go

    package mockgocql
    
    import (
        "fmt"
    
        "github.com/gocql/gocql"
    )
    
    type SessionImpl struct {
        session *gocql.Session
    }
    
    type QueryImpl struct {
        query *gocql.Query
    }
    
    func (s *SessionImpl) Query(stmt string, values ...interface{}) QueryInterface {
        query := s.session.Query(stmt, values)
        return &QueryImpl{query: query}
    }
    
    func (q *QueryImpl) Exec() error {
        return fmt.Errorf("test")
    }
    
    func (q *QueryImpl) Consistency(c gocql.Consistency) QueryInterface {
        return q
    }
    

    mock.go

    package mockgocql
    
    import (
        "github.com/gocql/gocql"
    )
    
    type SessionMock struct {
    }
    
    type QueryMock struct {
    }
    
    func (s *SessionMock) Query(stmt string, values ...interface{}) QueryInterface {
        return &QueryMock{}
    }
    
    func (q *QueryMock) Exec() error {
        return nil
    }
    
    func (q *QueryMock) Consistency(c gocql.Consistency) QueryInterface {
        return q
    }
    

    simple_test.go

    package mockgocql
    
    import (
        "testing"
    
        "github.com/stretchr/testify/assert"
    )
    
    func Test_simpleGetSeglistFromCassandra(t *testing.T) {
        mockCassandra := SessionMock{}
        err := simpleGetSeglistFromCassandra(&mockCassandra, "test")
        assert.NoError(t, err)
    }
    
    

    Reference

    I found others' trials here. https://github.com/gocql/gocql/issues/415 It seems they do similar things but it can be helpful to you.