fancy sql stubs

go get


mogi GoDoc Coverage

import ""

mogi is a fancy SQL mocking/stubbing library for Go. It uses the vitess SQL parser for maximum happiness.

Note: because vitess is a MySQL-flavored parser, other kinds of (non-)standard SQL may break mogi. Mogi isn't finished yet. You can't yet filter stubs based on subqueries and complex bits like ON DUPLICATED AT.


Getting started

import  ""
db, _ := sql.Open("mogi", "")

Stubbing SELECT queries

// Stub any SELECT query
mogi.Select().StubCSV(`1,Yona Yona Ale,Yo-Ho Brewing,5.5`)
rows, err := db.Query("SELECT id, name, brewery, pct FROM beer")

// Reset to clear all stubs

// Stub SELECT queries by columns selected
mogi.Select("id", "name", "brewery", "pct").StubCSV(`1,Yona Yona Ale,Yo-Ho Brewing,5.5`)
// Aliased columns should be given as they are aliased.
// Qualified columns should be given as they are qualified. 
// e.g. SELECT AS n, breweries.founded FROM beer JOIN breweries ON beer.brewery =
mogi.Select("n", "breweries.founded").StubCSV(`Stone IPA,1996`)

// You can stub with driver.Values instead of CSV
mogi.Select("id", "deleted_at").Stub([][]driver.Value{{1, nil}})

// Filter by table name
mogi.Select().From("beer").StubCSV(`1,Yona Yona Ale,Yo-Ho Brewing,5.5`)

// You can supply multiple table names for JOIN queries
// e.g. SELECT, FROM beer JOIN wine ON beer.pct = wine.pct
// or   SELECT, FROM beer, wine WHERE beer.pct = wine.pct
mogi.Select().From("beer", "wine").StubCSV(`Westvleteren XII,Across the Pond Riesling`)

// Filter by WHERE clause params
mogi.Select().Where("id", 10).StubCSV(`10,Apex,Bear Republic Brewing Co.,8.95`)
mogi.Select().Where("id", 42).StubCSV(`42,Westvleteren XII,Brouwerij Westvleteren,10.2`)
rows, err := db.Query("SELECT id, name, brewery, pct FROM beer WHERE id = ?", 10)
rows, err = db.Query("SELECT id, name, brewery, pct FROM beer WHERE id = ?", 42)

// Pass multiple arguments to Where() for IN clauses. 
mogi.Select().Where("id", 10, 42).StubCSV("Apex\nWestvleteren XII")
rows, err = db.Query("SELECT name FROM beer WHERE id IN (?, ?)", 10, 42)

// Stub an error while you're at it
mogi.Select().Where("id", 3).StubError(sql.ErrNoRows)
// FYI, unstubbed queries will return mogi.ErrUnstubbed

// Filter by args given 
mogi.Select().Args(1).StubCSV(`1,Yona Yona Ale,Yo-Ho Brewing,5.5`)
rows, err := db.Query("SELECT id, name, brewery, pct FROM beer WHERE id = ?", 1)

// Chain filters as much as you'd like
mogi.Select("id", "name", "brewery", "pct").From("beer").Where("id", 1).StubCSV(`1,Yona Yona Ale,Yo-Ho Brewing,5.5`)

Stubbing INSERT queries

// Stub any INSERT query
// You can use StubResult to easily stub a driver.Result. 
// You can pass -1 to StubResult to have it return an error for that particular bit.
// In this example, we have 1 row affected, but no LastInsertID. 
mogi.Insert().StubResult(-1, 1)
// If you have your own driver.Result you want to pass, just use Stub.
// You can also stub an error with StubError. 

// Filter by the columns used in the INSERT query
mogi.Insert("name", "brewery", "pct").StubResult(1, 1)
result, err := db.Exec("INSERT INTO beer (name, brewery, pct) VALUES (?, ?, ?)", "Yona Yona Ale", "Yo-Ho Brewing", 5.5)

// Filter by the table used in the query
mogi.Insert().Into("beer").StubResult(1, 1)

// Filter by the args passed to the query (the things replacing the ?s)
mogi.Insert().Args("Yona Yona Ale", "Yo-Ho Brewing", 5.5).StubResult(1, 1)

// Filter by the values used in the query
mogi.Insert().Value("name", "Yona Yona Ale").Value("brewery", "Yo-Ho Brewing").StubResult(1, 1)
// Use ValueAt when you are inserting multiple rows. The first argument is the row #, starting with 0.
// Parameters are interpolated for you.
    ValueAt(0, "brewery", "Mikkeller").ValueAt(0, "pct", 4.6).
    ValueAt(1, "brewery", "BrewDog").ValueAt(1, "pct", 18.2).
    StubResult(4, 2)
result, err = db.Exec(`INSERT INTO beer (name, brewery, pct) VALUES (?, "Mikkeller", 4.6), (?, ?, ?)`,
    "Mikkel’s Dream",
    "Tokyo*", "BrewDog", 18.2,

Stubbing UPDATE queries

// Stub any UPDATE query
// UPDATE stubs work the same as INSERT stubs
// This stubs all UPDATE queries to return 10 rows affected
mogi.Update().StubResult(-1, 10)
// This does the same thing

// Filter by the columns used in the SET clause
mogi.Update("name", "brewery", "pct").StubRowsAffected(1)
_, err := db.Exec(`UPDATE beer
                   SET name = "Mikkel’s Dream", brewery = "Mikkeller", pct = 4.6
                   WHERE id = ? AND moon = ?`, 3, "full")

// Filter by values set by the SET clause
mogi.Update().Value("name", "Mikkel’s Dream").Value("brewery", "Mikkeller").StubRowsAffected(1)

// Filter by args (? placeholder values)
mogi.Update().Args(3, "full").StubRowsAffected(1)

// Filter by the table being updated

// Filter by WHERE clause params
mogi.Update().Where("id", 3).Where("moon", "full").StubRowsAffected(1)

Stubbing DELETE queries

Works the same as UPDATE, docs later!

Other stuff


You can remove all the stubs you've set with mogi.Reset().


mogi.Verbose(true) will enable verbose mode, logging unstubbed queries.

Parse time

Set the time layout with mogi.ParseTime(). CSV values matching that layout will be converted to time.Time. You can also stub time.Time directly using the Stub() method.

        Where("id", 42).
Dump stubs

Dump all the stubs with mogi.Dump(). It will print something like this:

>>           Query stubs: (1 total)                         
#1    [3]    SELECT (any)                               [+1]
             FROM device_tokens                         [+1]
             WHERE user_id ≈ [42]                       [+1]
             → error: sql: no rows in result set                  

>>           Exec stubs: (2 total)                          
#1    [3]    INSERT (any)                               [+1]
             TABLE device_tokens                        [+1]
             VALUE device_type ≈ gunosy_lite (row 0)    [+1]
             → result ID: 1337, rows: 1                        
#2    [2]    INSERT (any)                               [+1]
             TABLE device_tokens                        [+1]
             → error: device_type should be overwriten    

This is helpful when you're debugging and need to double-check the priorities and conditions you've stubbed. The numbers in [brackets] are the priorities. You can also add Dump() to a stub condition chain. It will dump lots of information about the query when matched.