Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I test stuff that uses gocql? #415

Closed
willfaught opened this issue Jun 15, 2015 · 7 comments
Closed

How do I test stuff that uses gocql? #415

willfaught opened this issue Jun 15, 2015 · 7 comments
Labels

Comments

@willfaught
Copy link
Contributor

The most useful thing would be to have an in-memory fake implementation, but I don't even see a mockable interface. Is the best practice to wrap gocql with some kind of load/store interface and mock that instead?

@0x6e6562
Copy link
Member

#298 might provide some food for thought - there are also a bunch of other threads if you search for mock in the closed issues.

In terms of mockability, there hasn't been much traction with isolating a mockable interface. That is not to say we're against it per se, just that there is little motivation on the part of the maintenance team and in the community. That said, we do still have the TestServer definition in the test library, which we use for simple smoke testing.

Generally speaking, the answer to the question of how to test stuff with gocql is to boot a Cassandra instance and just start testing. We use ccm for our integration tests on Travis.

@Zariel
Copy link
Member

Zariel commented Jun 15, 2015

The method I use is to mock the layer just above gocql which is the load/store interface

@Zariel
Copy link
Member

Zariel commented Jun 19, 2015

I'm going to close this as it not really a bug, if you would like assistance then I would be happy to help on the mailing list or in gitter.

@thecadams
Copy link

thecadams commented Feb 5, 2017

Found this after some googling (and after eventually solving it on my own) and am leaving this here for the next person. I wanted to use gomock to test the code that directly interfaced with gocql, but without the overhead of doing I/O.

My approach was to wrap gocql.Session with code that should make the calling code look almost identical. The methods you may want to implement will be different, but this should make a decent starting point:

package stores

import "github.com/gocql/gocql"

// gocql does not use interfaces, which makes mocking impossible.
// So pass our own wrapper implementation around.
// This allows us to use gomock in tests.

// SessionInterface allows gomock mock of gocql.Session
type SessionInterface interface {
	Query(string, ...interface{}) QueryInterface
}

// QueryInterface allows gomock mock of gocql.Query
type QueryInterface interface {
	Bind(...interface{}) QueryInterface
	Exec() error
	Iter() IterInterface
	Scan(...interface{}) error
}

// IterInterface allows gomock mock of gocql.Iter
type IterInterface interface {
	Scan(...interface{}) bool
}

// 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...)
}

// Scan is a wrapper for the iter's Scan method
func (i *Iter) Scan(dest ...interface{}) bool {
	return i.iter.Scan(dest...)
}
func BuildSession(contactpoints []string) (stores.SessionInterface, error) {
	cluster := gocql.NewCluster(contactpoints...)
	session, err := cluster.CreateSession()
	if err != nil {
		return nil, fmt.Errorf("Unable to initialize Cassandra session: %v", err)
	}
	return stores.NewSession(session), nil
}

Now in test code you can pass in mockgen's generated mocks, and your code still reads like it's directly caling the underlying session. Hope that helps.

@willfaught
Copy link
Contributor Author

Check out https://github.com/willfaught/gockle, it does something similar to enable mocking.

@thecadams
Copy link

Thanks!

@Guilospanck
Copy link

Hi,
I've stumble across this issue with gocqlx and created some wrapper around the library.
You can check it out at igocqlx and gocqlxmock.

Also, if you wanna more information about how to use it than there are inside the README, you can check this repository which makes use of both of them.

Feel free to comment or help with it!
Cheers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants