Skip to content

Instantly share code, notes, and snippets.

@mrrizal
Last active February 22, 2023 07:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrrizal/bac93535df6fb5c79ffdc9ddd5a49882 to your computer and use it in GitHub Desktop.
Save mrrizal/bac93535df6fb5c79ffdc9ddd5a49882 to your computer and use it in GitHub Desktop.
Implementing Interfaces for GoCQL lib
func NewMockService(logger commons.Logger, session custom_gocql.SessionInterface) Service {
return &Service{session: session, logger: logger}
}
// InitMockFunc it's use for implelementing the interface.
// it's just mock, so just put empty function
func InitMockFunc() {
session.ExecutedQuery = []string{}
session.MockQuery = func(stmt string, val ...interface{}) custom_gocql.QueryInterface {
session.ExecutedQuery = append(session.ExecutedQuery, NormalizeString(stmt))
return &query
}
query.MockWithContext = func(ctx context.Context) custom_gocql.QueryInterface {
return &query
}
query.MockExec = func() error {
return nil
}
query.MockPageSize = func(i int) custom_gocql.QueryInterface {
return &query
}
query.MockWithContext = func(ctx context.Context) custom_gocql.QueryInterface {
return &query
}
query.MockIter = func() custom_gocql.IterInterface {
return &iter
}
iter.MockScanner = func() gocql.Scanner {
return scanner
}
scanner.MockNext = func() bool {
return true
}
scanner.MockScan = func(i ...interface{}) error {
return nil
}
}
// testSaveFeed function for test SaveFeed
func testSaveFeed(t *testing.T) {
expectedString := {"your expected query"}
expectedString = NormalizeString(expectedString)
RunTest(func() {
t.Run("SaveFeed.Ok", func(t *testing.T) {
// create mock service
svc := NewMockService(logger, &session)
// dont forget to call `InitMockFunc` to implement SaveFeed,
// if not it will raise error
err := svc.SaveFeed(ctx, &models.Feed{})
assert.Equal(t, nil, err)
assert.Equal(t, expectedString, session.ExecutedQuery[0])
})
})
RunTest(func() {
t.Run("SaveFeed.Error", func(t *testing.T) {
// at InitMockFunc, the MockExec func return success
// but for this test, we want the MockExec func return,
// so this the way how to achive that
query.MockExec = func() error {
return errors.New("error")
}
svc := NewMockService(logger, &session)
err := svc.SaveFeed(ctx, &models.Feed{})
assert.NotEqual(t, nil, err)
})
})
}
func NewService(logger commons.Logger, session *gocql.Session) Service {
// we use interface instead of directly gocql
s := custom_gocql.NewSession(session)
return &Service{session: s, logger: logger}
}
// SaveFeed function for saving feed to db
func (fs *service) SaveFeed(ctx context.Context, objFeed *models.Feed) error {
dataJSON, err := json.Marshal(objFeed.Data)
if err != nil {
return err
}
// at this statement, we not use "real" gocql, we use the interface
err = fs.session.Query({"your query"}).WithContext(ctx).Exec()
return err
}
// interface for gocql
package custom_gocql
import (
"context"
"github.com/gocql/gocql"
)
// SessionInterface allows gomock custom_gocql of gocql.Session
type SessionInterface interface {
Query(string, ...interface{}) QueryInterface
}
// QueryInterface allows gomock custom_gocql of gocql.Query
type QueryInterface interface {
Bind(...interface{}) QueryInterface
Exec() error
Iter() IterInterface
Scan(...interface{}) error
WithContext(context.Context) QueryInterface
PageSize(int) QueryInterface
}
// IterInterface allows gomock custom_gocql of gocql.Iter
type IterInterface interface {
Scan(...interface{}) bool
Scanner() gocql.Scanner
}
// Session is a wrapper for a session for mockability.
type Session struct {
session *gocql.Session
}
// Query is a wrapper for a query for mockability.
type Query struct {
query *gocql.Query
}
// Iter is a wrapper for an iter for mockability.
type Iter struct {
iter *gocql.Iter
}
// NewSession instantiates a new Session
func NewSession(session *gocql.Session) SessionInterface {
return &Session{
session,
}
}
// NewQuery instantiates a new Query
func NewQuery(query *gocql.Query) QueryInterface {
return &Query{
query,
}
}
// NewIter instantiates a new Iter
func NewIter(iter *gocql.Iter) IterInterface {
return &Iter{
iter,
}
}
// Query wraps the session's query method
func (s *Session) Query(stmt string, values ...interface{}) QueryInterface {
return NewQuery(s.session.Query(stmt, values...))
}
// Bind wraps the query's Bind method
func (q *Query) Bind(v ...interface{}) QueryInterface {
return NewQuery(q.query.Bind(v...))
}
// Exec wraps the query's Exec method
func (q *Query) Exec() error {
return q.query.Exec()
}
// Iter wraps the query's Iter method
func (q *Query) Iter() IterInterface {
return NewIter(q.query.Iter())
}
// Scan wraps the query's Scan method
func (q *Query) Scan(dest ...interface{}) error {
return q.query.Scan(dest...)
}
func (q *Query) WithContext(ctx context.Context) QueryInterface {
return NewQuery(q.query.WithContext(ctx))
}
func (q *Query) PageSize(n int) QueryInterface {
return NewQuery(q.query.PageSize(n))
}
// Scan is a wrapper for the iter's Scan method
func (i *Iter) Scan(dest ...interface{}) bool {
return i.iter.Scan(dest...)
}
func (i *Iter) Scanner() gocql.Scanner {
return i.iter.Scanner()
}
// mock use for mocking gocql interface
package custom_gocql
import (
"context"
"github.com/gocql/gocql"
)
type MockSession struct {
ExecutedQuery []string
MockQuery func(string, ...interface{}) QueryInterface
}
type MockQuery struct {
MockBind func(...interface{}) QueryInterface
MockExec func() error
MockIter func() IterInterface
MockScan func(...interface{}) error
MockWithContext func(context.Context) QueryInterface
MockPageSize func(int) QueryInterface
}
type MockIter struct {
mockScan func(...interface{}) bool
MockScanner func() gocql.Scanner
}
type MockScanner struct {
MockNext func() bool
MockScan func(...interface{}) error
MockErr func() error
}
func (s MockScanner) Next() bool {
return s.MockNext()
}
func (s MockScanner) Scan(val ...interface{}) error {
return s.MockScan(val...)
}
func (s MockScanner) Err() error {
return s.MockErr()
}
func NewMockSession() MockSession {
return MockSession{}
}
func NewMockQuery() MockQuery {
return MockQuery{}
}
func NewMockIter() MockIter {
return MockIter{}
}
func NewMockScanner() MockScanner {
return MockScanner{}
}
func (s *MockSession) Query(stmt string, val ...interface{}) QueryInterface {
return s.MockQuery(stmt, val...)
}
// Bind wraps the query's Bind method
func (q *MockQuery) Bind(v ...interface{}) QueryInterface {
return q.MockBind(v...)
}
// Exec wraps the query's Exec method
func (q *MockQuery) Exec() error {
return q.MockExec()
}
// Iter wraps the query's Iter method
func (q *MockQuery) Iter() IterInterface {
return q.MockIter()
}
// Scan wraps the query's Scan method
func (q *MockQuery) Scan(dest ...interface{}) error {
return q.MockScan(dest...)
}
func (q *MockQuery) WithContext(ctx context.Context) QueryInterface {
return q.MockWithContext(ctx)
}
func (q *MockQuery) PageSize(n int) QueryInterface {
return q.MockPageSize(n)
}
func (i *MockIter) Scan(val ...interface{}) bool {
return i.mockScan(val...)
}
func (i *MockIter) Scanner() gocql.Scanner {
return i.MockScanner()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment