kandi background
Explore Kits

go-sqlmock | Sql mock driver for golang to test database interactions | Unit Testing library

 by   DATA-DOG Go Version: v1.5.0 License: Non-SPDX

 by   DATA-DOG Go Version: v1.5.0 License: Non-SPDX

Download this library from

kandi X-RAY | go-sqlmock Summary

go-sqlmock is a Go library typically used in Testing, Unit Testing applications. go-sqlmock has no bugs, it has no vulnerabilities and it has medium support. However go-sqlmock has a Non-SPDX License. You can download it from GitHub.
Sql mock driver for golang to test database interactions
Support
Support
Quality
Quality
Security
Security
License
License
Reuse
Reuse

kandi-support Support

  • go-sqlmock has a medium active ecosystem.
  • It has 3537 star(s) with 289 fork(s). There are 36 watchers for this library.
  • It had no major release in the last 12 months.
  • There are 41 open issues and 140 have been closed. On average issues are closed in 34 days. There are 8 open pull requests and 0 closed requests.
  • It has a neutral sentiment in the developer community.
  • The latest version of go-sqlmock is v1.5.0
go-sqlmock Support
Best in #Unit Testing
Average in #Unit Testing
go-sqlmock Support
Best in #Unit Testing
Average in #Unit Testing

quality kandi Quality

  • go-sqlmock has 0 bugs and 0 code smells.
go-sqlmock Quality
Best in #Unit Testing
Average in #Unit Testing
go-sqlmock Quality
Best in #Unit Testing
Average in #Unit Testing

securitySecurity

  • go-sqlmock has no vulnerabilities reported, and its dependent libraries have no vulnerabilities reported.
  • go-sqlmock code analysis shows 0 unresolved vulnerabilities.
  • There are 0 security hotspots that need review.
go-sqlmock Security
Best in #Unit Testing
Average in #Unit Testing
go-sqlmock Security
Best in #Unit Testing
Average in #Unit Testing

license License

  • go-sqlmock has a Non-SPDX License.
  • Non-SPDX licenses can be open source with a non SPDX compliant license, or non open source licenses, and you need to review them closely before use.
go-sqlmock License
Best in #Unit Testing
Average in #Unit Testing
go-sqlmock License
Best in #Unit Testing
Average in #Unit Testing

buildReuse

  • go-sqlmock releases are available to install and integrate.
  • Installation instructions are not available. Examples and code snippets are available.
  • It has 5359 lines of code, 290 functions and 44 files.
  • It has high code complexity. Code complexity directly impacts maintainability of the code.
go-sqlmock Reuse
Best in #Unit Testing
Average in #Unit Testing
go-sqlmock Reuse
Best in #Unit Testing
Average in #Unit Testing
Top functions reviewed by kandi - BETA

Coming Soon for all Libraries!

Currently covering the most popular Java, JavaScript and Python libraries. See a SAMPLE HERE.
kandi's functional review helps you automatically verify the functionalities of the libraries and avoid rework.

go-sqlmock Key Features

Sql mock driver for golang to test database interactions

Install

copy iconCopydownload iconDownload
go get github.com/DATA-DOG/go-sqlmock

Something you may want to test, assuming you use the

copy iconCopydownload iconDownload
package main

import (
	"database/sql"

	_ "github.com/go-sql-driver/mysql"
)

func recordStats(db *sql.DB, userID, productID int64) (err error) {
	tx, err := db.Begin()
	if err != nil {
		return
	}

	defer func() {
		switch err {
		case nil:
			err = tx.Commit()
		default:
			tx.Rollback()
		}
	}()

	if _, err = tx.Exec("UPDATE products SET views = views + 1"); err != nil {
		return
	}
	if _, err = tx.Exec("INSERT INTO product_viewers (user_id, product_id) VALUES (?, ?)", userID, productID); err != nil {
		return
	}
	return
}

func main() {
	// @NOTE: the real connection is not required for tests
	db, err := sql.Open("mysql", "root@/blog")
	if err != nil {
		panic(err)
	}
	defer db.Close()

	if err = recordStats(db, 1 /*some user id*/, 5 /*some product id*/); err != nil {
		panic(err)
	}
}

Tests with sqlmock

copy iconCopydownload iconDownload
package main

import (
	"fmt"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
)

// a successful case
func TestShouldUpdateStats(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
	}
	defer db.Close()

	mock.ExpectBegin()
	mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1))
	mock.ExpectExec("INSERT INTO product_viewers").WithArgs(2, 3).WillReturnResult(sqlmock.NewResult(1, 1))
	mock.ExpectCommit()

	// now we execute our method
	if err = recordStats(db, 2, 3); err != nil {
		t.Errorf("error was not expected while updating stats: %s", err)
	}

	// we make sure that all expectations were met
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Errorf("there were unfulfilled expectations: %s", err)
	}
}

// a failing test case
func TestShouldRollbackStatUpdatesOnFailure(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
	}
	defer db.Close()

	mock.ExpectBegin()
	mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1))
	mock.ExpectExec("INSERT INTO product_viewers").
		WithArgs(2, 3).
		WillReturnError(fmt.Errorf("some error"))
	mock.ExpectRollback()

	// now we execute our method
	if err = recordStats(db, 2, 3); err == nil {
		t.Errorf("was expecting an error, but there was none")
	}

	// we make sure that all expectations were met
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Errorf("there were unfulfilled expectations: %s", err)
	}
}

Customize SQL query matching

copy iconCopydownload iconDownload
	db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))

Matching arguments like time.Time

copy iconCopydownload iconDownload
type AnyTime struct{}

// Match satisfies sqlmock.Argument interface
func (a AnyTime) Match(v driver.Value) bool {
	_, ok := v.(time.Time)
	return ok
}

func TestAnyTimeArgument(t *testing.T) {
	t.Parallel()
	db, mock, err := New()
	if err != nil {
		t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
	}
	defer db.Close()

	mock.ExpectExec("INSERT INTO users").
		WithArgs("john", AnyTime{}).
		WillReturnResult(NewResult(1, 1))

	_, err = db.Exec("INSERT INTO users(name, created_at) VALUES (?, ?)", "john", time.Now())
	if err != nil {
		t.Errorf("error '%s' was not expected, while inserting a row", err)
	}

	if err := mock.ExpectationsWereMet(); err != nil {
		t.Errorf("there were unfulfilled expectations: %s", err)
	}
}

Run tests

copy iconCopydownload iconDownload
go test -race

Issue with go-sqlmock testing in the expected query part

copy iconCopydownload iconDownload
    package unit
    
    import (
        "encoding/json"
        "fmt"
        "math/rand"
        "net/http"
        "net/http/httptest"
        "regexp"
        "testing"
    
        "github.com/DATA-DOG/go-sqlmock"
        "github.com/SamiAlsubhi/go/controllers"
        "github.com/SamiAlsubhi/go/routes"
        "github.com/gin-gonic/gin"
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
        "github.com/stretchr/testify/suite"
        "gorm.io/driver/postgres"
        "gorm.io/gorm"
    )
    
    type Suite struct {
        suite.Suite
        DB     *gorm.DB
        mock   sqlmock.Sqlmock
        router *gin.Engine
    }
    
    func (s *Suite) SetupSuite() {
        //t.Logf("setup start")
        conn, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherRegexp))
        if err != nil || conn == nil {
            panic(fmt.Sprintf("Failed to open mock sql db, got error: %v", err))
        }
        s.mock = mock
    
        dialector := postgres.New(postgres.Config{
            DSN:                  "sqlmock_db_0",
            DriverName:           "postgres",
            Conn:                 conn,
            PreferSimpleProtocol: true,
        })
    
        if db, err := gorm.Open(dialector, &gorm.Config{SkipDefaultTransaction: true}); err != nil || db == nil {
            panic(fmt.Sprintf("Failed to open gorm v2 db, got error: %v", err))
        } else {
            s.DB = db
        }
    
        api := &controllers.API{Db: s.DB, IsTesting: true}
        s.router = routes.SetupRouter(api)
    
    }
    
    func TestSetup(t *testing.T) {
    
        suite.Run(t, new(Suite))
    
    }
    
    // func (s *Suite) AfterTest(_, _ string) {
    //  require.NoError(s.T(), s.mock.ExpectationsWereMet())
    // }
    
    func (s *Suite) Test_GetOTP_Non_Existing_Phone() {
        /*
            This to test getting OTP for a phone number that does not exist in the otps table
        */
        phone := fmt.Sprintf("%v", 90000000+rand.Intn(99999999-90000000))
        s.mock.MatchExpectationsInOrder(false)
    
        s.mock.ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "otps" WHERE phone = $1 AND "otps"."deleted_at" IS NULL`)).
            WithArgs(phone).
            WillReturnRows(sqlmock.NewRows([]string{"count"}).
                AddRow(0))
    
        s.mock.ExpectQuery(regexp.QuoteMeta(
            `INSERT INTO "otps" ("created_at","updated_at","deleted_at","phone","code") VALUES ($1,$2,$3,$4,$5) RETURNING "id"`)).
            WithArgs(sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), phone, sqlmock.AnyArg()).
            WillReturnRows(sqlmock.NewRows([]string{"id"}).
                AddRow(1))
    
        w := httptest.NewRecorder()
        req, err := http.NewRequest("GET", "/api/auth/get-otp/"+phone, nil)
        require.NoError(s.T(), err)
    
        s.router.ServeHTTP(w, req)
        assert.Equal(s.T(), 200, w.Code)
        //parse response
        var response gin.H
        err = json.Unmarshal(w.Body.Bytes(), &response)
        require.NoError(s.T(), err)
        _, ok := response["expiry_in"]
        assert.True(s.T(), ok)
    
        require.NoError(s.T(), s.mock.ExpectationsWereMet())
    
    }

`could not match actual sql` error while mocking gorm `updates` using go-sqlmock?

copy iconCopydownload iconDownload
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))

How to mock gorm insert with go-sql (postgres)

copy iconCopydownload iconDownload
mock.ExpectBegin()
mock.ExpectExec(regexp.QuoteMeta("INSERT INTO \"tests\" (\"first_name\") VALUES (?)")).WithArgs("c").WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectCommit()

Community Discussions

Trending Discussions on go-sqlmock
  • Issue with go-sqlmock testing in the expected query part
  • Creating a gorm database with go-sqlmock (runtime error)
  • DB Mocking in one Go test case is interfering with other test case
  • How to use go-sqlmock when I have concurrent query in my program?
  • Go sqlmock SELECT missing expected query
  • Testing Golang function containing call to sql.Open connection without a DB
  • `could not match actual sql` error while mocking gorm `updates` using go-sqlmock?
  • How to mock gorm insert with go-sql (postgres)
Trending Discussions on go-sqlmock

QUESTION

Issue with go-sqlmock testing in the expected query part

Asked 2022-Jan-24 at 08:37

I am using go-sqlmock for the first time and I am trying to write a test for post operation. I am using gorm and gin.

  1. The test is giving me an error where s.mock.ExpectQuery(regexp.QuoteMeta(.... I am not what is the issue here. I have posted both the test and the output.
  2. Also, (this has nothing to do with 1) in this test I really do not know what the code will be as it is randomly generated in the api controller. Is there a way to assign a generic number in the code field.

The test file

     package unit
    
     import (
        "net/http"
        "net/http/httptest"
        "regexp"
        "testing"
    
        "github.com/DATA-DOG/go-sqlmock"
        "github.com/SamiAlsubhi/go/controllers"
        "github.com/SamiAlsubhi/go/routes"
        "github.com/gin-gonic/gin"
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
        "github.com/stretchr/testify/suite"
        "gorm.io/driver/postgres"
        "gorm.io/gorm"
     )
    
    type Suite struct {
        suite.Suite
        DB     *gorm.DB
        mock   sqlmock.Sqlmock
        router *gin.Engine
    }
    
    func (s *Suite) SetupSuite(t *testing.T) {
        conn, mock, err := sqlmock.New()
        if err != nil || conn == nil {
            t.Errorf("Failed to open mock sql db, got error: %v", err)
        }
        s.mock = mock
    
        dialector := postgres.New(postgres.Config{
            DSN:                  "sqlmock_db_0",
            DriverName:           "postgres",
            Conn:                 conn,
            PreferSimpleProtocol: true,
        })
    
        if db, err := gorm.Open(dialector, &gorm.Config{}); err != nil || db == nil {
            t.Errorf("Failed to open gorm v2 db, got error: %v", err)
        } else {
            s.DB = db
        }
        api := &controllers.API{Db: s.DB}
    
        s.router = routes.SetupRouter(api)
    
    }
    
    func TestSetup(t *testing.T) {
        suite.Run(t, new(Suite))
    
    }
    
    func (s *Suite) AfterTest(_, _ string) {
        require.NoError(s.T(), s.mock.ExpectationsWereMet())
    }
    
    func (s *Suite) Test_GetOTP() {
        var (
            phone = "99999999"
            code  = "123456"
        )
    
        s.mock.ExpectQuery(regexp.QuoteMeta(
            `INSERT INTO "otps" ("phone","code") VALUES ($1,$2) RETURNING "otps"."id"`)).
            WithArgs(phone, code).
            WillReturnRows(sqlmock.NewRows([]string{"id"}).
                AddRow(1))
    
        s.mock.ExpectCommit()
    
        w := httptest.NewRecorder()
        req, err := http.NewRequest("GET", "/api/auth/get-otp/"+phone, nil)
        require.NoError(s.T(), err)
    
        s.router.ServeHTTP(w, req)
        assert.Equal(s.T(), 200, w.Code)
    
        //require.Nil(s.T(), deep.Equal(&model.Person{ID: id, Name: name}, w.Body))
    }

the output.

    --- FAIL: TestSetup (0.00s)
        --- FAIL: TestSetup/Test_GetOTP (0.00s)
            /Users/sami/Desktop/SamiAlsubhi/go/test/unit/suite.go:63: test panicked: runtime error: invalid memory address or nil pointer dereference
                goroutine 26 [running]:
                runtime/debug.Stack()
                    /usr/local/go/src/runtime/debug/stack.go:24 +0x65
                github.com/stretchr/testify/suite.failOnPanic(0xc000001a00)
                    /Users/sami/Desktop/golang/pkg/mod/github.com/stretchr/testify@v1.7.0/suite/suite.go:63 +0x3e
                panic({0x49e96a0, 0x5193810})
                    /usr/local/go/src/runtime/panic.go:1038 +0x215
                github.com/SamiAlsubhi/go/test/unit.(*Suite).AfterTest(0x4abe61b, {0x4becfd0, 0xc000468940}, {0x0, 0x0})
                    /Users/sami/Desktop/SamiAlsubhi/go/test/unit/setup_test.go:60 +0x1c
                github.com/stretchr/testify/suite.Run.func1.1()
                    /Users/sami/Desktop/golang/pkg/mod/github.com/stretchr/testify@v1.7.0/suite/suite.go:137 +0x1b7
                panic({0x49e96a0, 0x5193810})
                    /usr/local/go/src/runtime/panic.go:1038 +0x215
                github.com/SamiAlsubhi/go/test/unit.(*Suite).Test_GetOTP(0xc000468940)
                    /Users/sami/Desktop/SamiAlsubhi/go/test/unit/setup_test.go:69 +0x4f
                reflect.Value.call({0xc000049140, 0xc000010308, 0x13}, {0x4abf50c, 0x4}, {0xc000080e70, 0x1, 0x1})
                    /usr/local/go/src/reflect/value.go:543 +0x814
                reflect.Value.Call({0xc000049140, 0xc000010308, 0xc000468940}, {0xc0003c9e70, 0x1, 0x1})
                    /usr/local/go/src/reflect/value.go:339 +0xc5
                github.com/stretchr/testify/suite.Run.func1(0xc000001a00)
                    /Users/sami/Desktop/golang/pkg/mod/github.com/stretchr/testify@v1.7.0/suite/suite.go:158 +0x4b6
                testing.tRunner(0xc000001a00, 0xc000162000)
                    /usr/local/go/src/testing/testing.go:1259 +0x102
                created by testing.(*T).Run
                    /usr/local/go/src/testing/testing.go:1306 +0x35a
    FAIL
    coverage: [no statements]
    FAIL    github.com/SamiAlsubhi/go/test/unit 0.912s
    FAIL

ANSWER

Answered 2022-Jan-24 at 08:37

Solution to the first issue:
when using testify/suite, There are bunch of methods if created for the Suite struct, they will be automatically executed when running the test. That being said, These methods will pass through an interface filter. In the case of .SetupSuite, it has to have NO arguments and No return, in order to run.

Solution to the second issue: There is a way in go-sqlmock to match any kind of data by using sqlmock.AnyArg().

Fixed code:

    package unit
    
    import (
        "encoding/json"
        "fmt"
        "math/rand"
        "net/http"
        "net/http/httptest"
        "regexp"
        "testing"
    
        "github.com/DATA-DOG/go-sqlmock"
        "github.com/SamiAlsubhi/go/controllers"
        "github.com/SamiAlsubhi/go/routes"
        "github.com/gin-gonic/gin"
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
        "github.com/stretchr/testify/suite"
        "gorm.io/driver/postgres"
        "gorm.io/gorm"
    )
    
    type Suite struct {
        suite.Suite
        DB     *gorm.DB
        mock   sqlmock.Sqlmock
        router *gin.Engine
    }
    
    func (s *Suite) SetupSuite() {
        //t.Logf("setup start")
        conn, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherRegexp))
        if err != nil || conn == nil {
            panic(fmt.Sprintf("Failed to open mock sql db, got error: %v", err))
        }
        s.mock = mock
    
        dialector := postgres.New(postgres.Config{
            DSN:                  "sqlmock_db_0",
            DriverName:           "postgres",
            Conn:                 conn,
            PreferSimpleProtocol: true,
        })
    
        if db, err := gorm.Open(dialector, &gorm.Config{SkipDefaultTransaction: true}); err != nil || db == nil {
            panic(fmt.Sprintf("Failed to open gorm v2 db, got error: %v", err))
        } else {
            s.DB = db
        }
    
        api := &controllers.API{Db: s.DB, IsTesting: true}
        s.router = routes.SetupRouter(api)
    
    }
    
    func TestSetup(t *testing.T) {
    
        suite.Run(t, new(Suite))
    
    }
    
    // func (s *Suite) AfterTest(_, _ string) {
    //  require.NoError(s.T(), s.mock.ExpectationsWereMet())
    // }
    
    func (s *Suite) Test_GetOTP_Non_Existing_Phone() {
        /*
            This to test getting OTP for a phone number that does not exist in the otps table
        */
        phone := fmt.Sprintf("%v", 90000000+rand.Intn(99999999-90000000))
        s.mock.MatchExpectationsInOrder(false)
    
        s.mock.ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "otps" WHERE phone = $1 AND "otps"."deleted_at" IS NULL`)).
            WithArgs(phone).
            WillReturnRows(sqlmock.NewRows([]string{"count"}).
                AddRow(0))
    
        s.mock.ExpectQuery(regexp.QuoteMeta(
            `INSERT INTO "otps" ("created_at","updated_at","deleted_at","phone","code") VALUES ($1,$2,$3,$4,$5) RETURNING "id"`)).
            WithArgs(sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), phone, sqlmock.AnyArg()).
            WillReturnRows(sqlmock.NewRows([]string{"id"}).
                AddRow(1))
    
        w := httptest.NewRecorder()
        req, err := http.NewRequest("GET", "/api/auth/get-otp/"+phone, nil)
        require.NoError(s.T(), err)
    
        s.router.ServeHTTP(w, req)
        assert.Equal(s.T(), 200, w.Code)
        //parse response
        var response gin.H
        err = json.Unmarshal(w.Body.Bytes(), &response)
        require.NoError(s.T(), err)
        _, ok := response["expiry_in"]
        assert.True(s.T(), ok)
    
        require.NoError(s.T(), s.mock.ExpectationsWereMet())
    
    }

Source https://stackoverflow.com/questions/70821998

Community Discussions, Code Snippets contain sources that include Stack Exchange Network

Vulnerabilities

No vulnerabilities reported

Install go-sqlmock

You can download it from GitHub.

Support

Visit godoc for general examples and public api reference. See .travis.yml for supported go versions. Different use case, is to functionally test with a real database - go-txdb all database related actions are isolated within a single transaction so the database can remain in the same state.

DOWNLOAD this Library from

Find, review, and download reusable Libraries, Code Snippets, Cloud APIs from
over 430 million Knowledge Items
Find more libraries
Reuse Solution Kits and Libraries Curated by Popular Use Cases

Save this library and start creating your kit

Explore Related Topics

Share this Page

share link
Reuse Pre-built Kits with go-sqlmock
Consider Popular Unit Testing Libraries
Compare Unit Testing Libraries with Highest Support
Find, review, and download reusable Libraries, Code Snippets, Cloud APIs from
over 430 million Knowledge Items
Find more libraries
Reuse Solution Kits and Libraries Curated by Popular Use Cases

Save this library and start creating your kit

  • © 2022 Open Weaver Inc.