├── main.go ├── go.mod ├── repository ├── repository.go ├── mysql │ ├── mysql.go │ └── mysql_test.go └── postgres │ ├── postgres.go │ └── postgres_test.go ├── .gitignore ├── .circleci └── config.yml ├── LICENSE ├── README.md └── go.sum /main.go: -------------------------------------------------------------------------------- 1 | // 2 | // go-unit-test-sql 3 | // 4 | // Copyright © 2020. All rights reserved. 5 | // 6 | 7 | package main 8 | 9 | func main() { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/moemoe89/go-unit-test-sql 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/DATA-DOG/go-sqlmock v1.4.1 7 | github.com/go-sql-driver/mysql v1.5.0 8 | github.com/google/uuid v1.1.1 9 | github.com/lib/pq v1.7.0 10 | github.com/stretchr/testify v1.6.1 11 | ) 12 | -------------------------------------------------------------------------------- /repository/repository.go: -------------------------------------------------------------------------------- 1 | // 2 | // go-unit-test-sql 3 | // 4 | // Copyright © 2020. All rights reserved. 5 | // 6 | 7 | package repository 8 | 9 | // Repository represent the repositories 10 | type Repository interface { 11 | Close() 12 | FindByID(id string) (*UserModel, error) 13 | Find() ([]*UserModel, error) 14 | Create(user *UserModel) error 15 | Update(user *UserModel) error 16 | Delete(id string) error 17 | } 18 | 19 | // UserModel represent the user model 20 | type UserModel struct { 21 | ID string 22 | Name string 23 | Email string 24 | Phone string 25 | } 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | # Log file 27 | *.log 28 | 29 | # Key files 30 | inuit_rsa 31 | inuit_rsa.pub 32 | 33 | # PID file 34 | *.pid 35 | 36 | # Temp vim files 37 | *.swp 38 | 39 | # MAC file 40 | .DS_Store 41 | 42 | #Intellij files 43 | .idea 44 | *.iml 45 | 46 | # binary 47 | go-unit-test-sql 48 | 49 | # vendor 50 | vendor 51 | 52 | # out 53 | *.out 54 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | test: 4 | working_directory: /go/src/github.com/moemoe89/go-unit-test-sql 5 | docker: 6 | - image: golang:1.14 7 | steps: 8 | - checkout 9 | - restore_cache: &restore_cache 10 | name: Restore go modules cache 11 | keys: 12 | - go-mod-{{ checksum "go.sum" }} 13 | - run: &go_mod_download 14 | name: Download go modules to cache 15 | command: go mod download 16 | - save_cache: &save_cache 17 | name: Save go modules cache 18 | key: go-mod-{{ checksum "go.sum" }} 19 | paths: 20 | - /go/pkg/mod/cache 21 | - run: 22 | name: Run unit tests and measure code coverage 23 | command: | 24 | go test -v -coverprofile=coverage.out ./... 25 | bash <(curl -s https://codecov.io/bash) -P ${CODECOV_TOKEN##*/} 26 | workflows: 27 | version: 2 28 | test: 29 | jobs: 30 | - test: 31 | filters: 32 | branches: 33 | only: /.*/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Bismo Baruno 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CircleCI](https://circleci.com/gh/moemoe89/go-unit-test-sql.svg?style=svg)](https://circleci.com/gh/moemoe89/go-unit-test-sql) 2 | [![codecov](https://codecov.io/gh/moemoe89/go-unit-test-sql/branch/master/graph/badge.svg)](https://codecov.io/gh/moemoe89/go-unit-test-sql) 3 | [![Go Report Card](https://goreportcard.com/badge/github.com/moemoe89/go-unit-test-sql)](https://goreportcard.com/report/github.com/moemoe89/go-unit-test-sql) 4 | 5 | # GO-UNIT-TEST-SQL # 6 | 7 | Example Mock Unit Test for SQL in Golang 8 | 9 | ## Directory structure 10 | Your project directory structure should look like this 11 | ``` 12 | + your_gopath/ 13 | | 14 | +--+ src/github.com/moemoe89 15 | | | 16 | | +--+ go-unit-test-sql/ 17 | | | 18 | | +--+ main.go 19 | | + repository/ 20 | | | 21 | | +--+ repository.go 22 | | | 23 | | +--+ mysql 24 | | | | 25 | | | +--+ mysql.go 26 | | | + mysql_test.go 27 | | | 28 | | +--+ postgres 29 | | | 30 | | +--+ postgres.go 31 | | + postgres_test.go 32 | | 33 | +--+ bin/ 34 | | | 35 | | +-- ... executable file 36 | | 37 | +--+ pkg/ 38 | | 39 | +-- ... all dependency_library required 40 | 41 | ``` 42 | 43 | ## Setup 44 | 45 | * Setup Golang 46 | * Setup Docker 47 | * Under `$GOPATH`, do the following command : 48 | ``` 49 | $ mkdir -p src/github.com/moemoe89 50 | $ cd src/github.com/moemoe89 51 | $ git clone 52 | $ mv go-unit-test-sql 53 | ``` 54 | 55 | ## How to Run Test 56 | ``` 57 | $ go test ./... 58 | ``` 59 | 60 | ## License 61 | 62 | MIT -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM= 2 | github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= 3 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= 6 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 7 | github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= 8 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 9 | github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= 10 | github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 11 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 12 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 13 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 14 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 15 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 16 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 17 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 18 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 19 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 20 | -------------------------------------------------------------------------------- /repository/mysql/mysql.go: -------------------------------------------------------------------------------- 1 | // 2 | // go-unit-test-sql 3 | // 4 | // Copyright © 2020. All rights reserved. 5 | // 6 | 7 | package mysql 8 | 9 | import ( 10 | "context" 11 | "database/sql" 12 | "time" 13 | 14 | repo "github.com/moemoe89/go-unit-test-sql/repository" 15 | 16 | _ "github.com/go-sql-driver/mysql" 17 | ) 18 | 19 | // repository represent the repository model 20 | type repository struct { 21 | db *sql.DB 22 | } 23 | 24 | // NewRepository will create a variable that represent the Repository struct 25 | func NewRepository(dialect, dsn string, idleConn, maxConn int) (repo.Repository, error) { 26 | db, err := sql.Open(dialect, dsn) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | err = db.Ping() 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | db.SetMaxIdleConns(idleConn) 37 | db.SetMaxOpenConns(maxConn) 38 | 39 | return &repository{db}, nil 40 | } 41 | 42 | // Close attaches the provider and close the connection 43 | func (r *repository) Close() { 44 | r.db.Close() 45 | } 46 | 47 | // FindByID attaches the user repository and find data based on id 48 | func (r *repository) FindByID(id string) (*repo.UserModel, error) { 49 | user := new(repo.UserModel) 50 | 51 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 52 | defer cancel() 53 | 54 | err := r.db.QueryRowContext(ctx, "SELECT id, name, email, phone FROM users WHERE id = ?", id).Scan(&user.ID, &user.Name, &user.Email, &user.Phone) 55 | if err != nil { 56 | return nil, err 57 | } 58 | return user, nil 59 | } 60 | 61 | // Find attaches the user repository and find all data 62 | func (r *repository) Find() ([]*repo.UserModel, error) { 63 | users := make([]*repo.UserModel, 0) 64 | 65 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 66 | defer cancel() 67 | 68 | rows, err := r.db.QueryContext(ctx, "SELECT id, name, email, phone FROM users") 69 | if err != nil { 70 | return nil, err 71 | } 72 | defer rows.Close() 73 | 74 | for rows.Next() { 75 | user := new(repo.UserModel) 76 | err = rows.Scan( 77 | &user.ID, 78 | &user.Name, 79 | &user.Email, 80 | &user.Phone, 81 | ) 82 | 83 | if err != nil { 84 | return nil, err 85 | } 86 | users = append(users, user) 87 | } 88 | 89 | return users, nil 90 | } 91 | 92 | // Create attaches the user repository and creating the data 93 | func (r *repository) Create(user *repo.UserModel) error { 94 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 95 | defer cancel() 96 | 97 | query := "INSERT INTO users (id, name, email, phone) VALUES (?, ?, ?, ?)" 98 | stmt, err := r.db.PrepareContext(ctx, query) 99 | if err != nil { 100 | return err 101 | } 102 | defer stmt.Close() 103 | 104 | _, err = stmt.ExecContext(ctx, user.ID, user.Name, user.Email, user.Phone) 105 | return err 106 | } 107 | 108 | // Update attaches the user repository and update data based on id 109 | func (r *repository) Update(user *repo.UserModel) error { 110 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 111 | defer cancel() 112 | 113 | query := "UPDATE users SET name = ?, email = ?, phone = ? WHERE id = ?" 114 | stmt, err := r.db.PrepareContext(ctx, query) 115 | if err != nil { 116 | return err 117 | } 118 | defer stmt.Close() 119 | 120 | _, err = stmt.ExecContext(ctx, user.Name, user.Email, user.Phone, user.ID) 121 | return err 122 | } 123 | 124 | // Delete attaches the user repository and delete data based on id 125 | func (r *repository) Delete(id string) error { 126 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 127 | defer cancel() 128 | 129 | query := "DELETE FROM users WHERE id = ?" 130 | stmt, err := r.db.PrepareContext(ctx, query) 131 | if err != nil { 132 | return err 133 | } 134 | defer stmt.Close() 135 | 136 | _, err = stmt.ExecContext(ctx, id) 137 | return err 138 | } 139 | -------------------------------------------------------------------------------- /repository/postgres/postgres.go: -------------------------------------------------------------------------------- 1 | // 2 | // go-unit-test-sql 3 | // 4 | // Copyright © 2020. All rights reserved. 5 | // 6 | 7 | package postgres 8 | 9 | import ( 10 | "context" 11 | "database/sql" 12 | "time" 13 | 14 | repo "github.com/moemoe89/go-unit-test-sql/repository" 15 | 16 | _ "github.com/lib/pq" 17 | ) 18 | 19 | // repository represent the repository model 20 | type repository struct { 21 | db *sql.DB 22 | } 23 | 24 | // NewRepository will create a variable that represent the Repository struct 25 | func NewRepository(dialect, dsn string, idleConn, maxConn int) (repo.Repository, error) { 26 | db, err := sql.Open(dialect, dsn) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | err = db.Ping() 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | db.SetMaxIdleConns(idleConn) 37 | db.SetMaxOpenConns(maxConn) 38 | 39 | return &repository{db}, nil 40 | } 41 | 42 | // Close attaches the provider and close the connection 43 | func (r *repository) Close() { 44 | r.db.Close() 45 | } 46 | 47 | // FindByID attaches the user repository and find data based on id 48 | func (r *repository) FindByID(id string) (*repo.UserModel, error) { 49 | user := new(repo.UserModel) 50 | 51 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 52 | defer cancel() 53 | 54 | err := r.db.QueryRowContext(ctx, "SELECT id, name, email, phone FROM users WHERE id = $1", id).Scan(&user.ID, &user.Name, &user.Email, &user.Phone) 55 | if err != nil { 56 | return nil, err 57 | } 58 | return user, nil 59 | } 60 | 61 | // Find attaches the user repository and find all data 62 | func (r *repository) Find() ([]*repo.UserModel, error) { 63 | users := make([]*repo.UserModel, 0) 64 | 65 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 66 | defer cancel() 67 | 68 | rows, err := r.db.QueryContext(ctx, "SELECT id, name, email, phone FROM users") 69 | if err != nil { 70 | return nil, err 71 | } 72 | defer rows.Close() 73 | 74 | for rows.Next() { 75 | user := new(repo.UserModel) 76 | err = rows.Scan( 77 | &user.ID, 78 | &user.Name, 79 | &user.Email, 80 | &user.Phone, 81 | ) 82 | 83 | if err != nil { 84 | return nil, err 85 | } 86 | users = append(users, user) 87 | } 88 | 89 | return users, nil 90 | } 91 | 92 | // Create attaches the user repository and creating the data 93 | func (r *repository) Create(user *repo.UserModel) error { 94 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 95 | defer cancel() 96 | 97 | query := "INSERT INTO users (id, name, email, phone) VALUES ($1, $2, $3, $4)" 98 | stmt, err := r.db.PrepareContext(ctx, query) 99 | if err != nil { 100 | return err 101 | } 102 | defer stmt.Close() 103 | 104 | _, err = stmt.ExecContext(ctx, user.ID, user.Name, user.Email, user.Phone) 105 | return err 106 | } 107 | 108 | // Update attaches the user repository and update data based on id 109 | func (r *repository) Update(user *repo.UserModel) error { 110 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 111 | defer cancel() 112 | 113 | query := "UPDATE users SET name = $1, email = $2, phone = $3 WHERE id = $4" 114 | stmt, err := r.db.PrepareContext(ctx, query) 115 | if err != nil { 116 | return err 117 | } 118 | defer stmt.Close() 119 | 120 | _, err = stmt.ExecContext(ctx, user.Name, user.Email, user.Phone, user.ID) 121 | return err 122 | } 123 | 124 | // Delete attaches the user repository and delete data based on id 125 | func (r *repository) Delete(id string) error { 126 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 127 | defer cancel() 128 | 129 | query := "DELETE FROM users WHERE id = $1" 130 | stmt, err := r.db.PrepareContext(ctx, query) 131 | if err != nil { 132 | return err 133 | } 134 | defer stmt.Close() 135 | 136 | _, err = stmt.ExecContext(ctx, id) 137 | return err 138 | } 139 | -------------------------------------------------------------------------------- /repository/mysql/mysql_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // go-unit-test-sql 3 | // 4 | // Copyright © 2020. All rights reserved. 5 | // 6 | 7 | package mysql 8 | 9 | import ( 10 | "database/sql" 11 | "log" 12 | "testing" 13 | 14 | r "github.com/moemoe89/go-unit-test-sql/repository" 15 | 16 | "github.com/DATA-DOG/go-sqlmock" 17 | "github.com/google/uuid" 18 | "github.com/stretchr/testify/assert" 19 | ) 20 | 21 | var u = &r.UserModel{ 22 | ID: uuid.New().String(), 23 | Name: "Momo", 24 | Email: "momo@mail.com", 25 | Phone: "08123456789", 26 | } 27 | 28 | func NewMock() (*sql.DB, sqlmock.Sqlmock) { 29 | db, mock, err := sqlmock.New() 30 | if err != nil { 31 | log.Fatalf("an error '%s' was not expected when opening a stub database connection", err) 32 | } 33 | 34 | return db, mock 35 | } 36 | 37 | func TestFindByID(t *testing.T) { 38 | db, mock := NewMock() 39 | repo := &repository{db} 40 | defer func() { 41 | repo.Close() 42 | }() 43 | 44 | query := "SELECT id, name, email, phone FROM users WHERE id = \\?" 45 | 46 | rows := sqlmock.NewRows([]string{"id", "name", "email", "phone"}). 47 | AddRow(u.ID, u.Name, u.Email, u.Phone) 48 | 49 | mock.ExpectQuery(query).WithArgs(u.ID).WillReturnRows(rows) 50 | 51 | user, err := repo.FindByID(u.ID) 52 | assert.NotNil(t, user) 53 | assert.NoError(t, err) 54 | } 55 | 56 | func TestFindByIDError(t *testing.T) { 57 | db, mock := NewMock() 58 | repo := &repository{db} 59 | defer func() { 60 | repo.Close() 61 | }() 62 | 63 | query := "SELECT id, name, email, phone FROM user WHERE id = \\?" 64 | 65 | rows := sqlmock.NewRows([]string{"id", "name", "email", "phone"}) 66 | 67 | mock.ExpectQuery(query).WithArgs(u.ID).WillReturnRows(rows) 68 | 69 | user, err := repo.FindByID(u.ID) 70 | assert.Empty(t, user) 71 | assert.Error(t, err) 72 | } 73 | 74 | func TestFind(t *testing.T) { 75 | db, mock := NewMock() 76 | repo := &repository{db} 77 | defer func() { 78 | repo.Close() 79 | }() 80 | 81 | query := "SELECT id, name, email, phone FROM users" 82 | 83 | rows := sqlmock.NewRows([]string{"id", "name", "email", "phone"}). 84 | AddRow(u.ID, u.Name, u.Email, u.Phone) 85 | 86 | mock.ExpectQuery(query).WillReturnRows(rows) 87 | 88 | users, err := repo.Find() 89 | assert.NotEmpty(t, users) 90 | assert.NoError(t, err) 91 | assert.Len(t, users, 1) 92 | } 93 | 94 | func TestCreate(t *testing.T) { 95 | db, mock := NewMock() 96 | repo := &repository{db} 97 | defer func() { 98 | repo.Close() 99 | }() 100 | 101 | query := "INSERT INTO users \\(id, name, email, phone\\) VALUES \\(\\?, \\?, \\?, \\?\\)" 102 | 103 | prep := mock.ExpectPrepare(query) 104 | prep.ExpectExec().WithArgs(u.ID, u.Name, u.Email, u.Phone).WillReturnResult(sqlmock.NewResult(0, 1)) 105 | 106 | err := repo.Create(u) 107 | assert.NoError(t, err) 108 | } 109 | 110 | func TestCreateError(t *testing.T) { 111 | db, mock := NewMock() 112 | repo := &repository{db} 113 | defer func() { 114 | repo.Close() 115 | }() 116 | 117 | query := "INSERT INTO user \\(id, name, email, phone\\) VALUES \\(\\?, \\?, \\?, \\?\\)" 118 | 119 | prep := mock.ExpectPrepare(query) 120 | prep.ExpectExec().WithArgs(u.ID, u.Name, u.Email, u.Phone).WillReturnResult(sqlmock.NewResult(0, 0)) 121 | 122 | err := repo.Create(u) 123 | assert.Error(t, err) 124 | } 125 | 126 | func TestUpdate(t *testing.T) { 127 | db, mock := NewMock() 128 | repo := &repository{db} 129 | defer func() { 130 | repo.Close() 131 | }() 132 | 133 | query := "UPDATE users SET name = \\?, email = \\?, phone = \\? WHERE id = \\?" 134 | 135 | prep := mock.ExpectPrepare(query) 136 | prep.ExpectExec().WithArgs(u.Name, u.Email, u.Phone, u.ID).WillReturnResult(sqlmock.NewResult(0, 1)) 137 | 138 | err := repo.Update(u) 139 | assert.NoError(t, err) 140 | } 141 | 142 | func TestUpdateErr(t *testing.T) { 143 | db, mock := NewMock() 144 | repo := &repository{db} 145 | defer func() { 146 | repo.Close() 147 | }() 148 | 149 | query := "UPDATE user SET name = \\?, email = \\?, phone = \\? WHERE id = \\?" 150 | 151 | prep := mock.ExpectPrepare(query) 152 | prep.ExpectExec().WithArgs(u.Name, u.Email, u.Phone, u.ID).WillReturnResult(sqlmock.NewResult(0, 0)) 153 | 154 | err := repo.Update(u) 155 | assert.Error(t, err) 156 | } 157 | 158 | func TestDelete(t *testing.T) { 159 | db, mock := NewMock() 160 | repo := &repository{db} 161 | defer func() { 162 | repo.Close() 163 | }() 164 | 165 | query := "DELETE FROM users WHERE id = \\?" 166 | 167 | prep := mock.ExpectPrepare(query) 168 | prep.ExpectExec().WithArgs(u.ID).WillReturnResult(sqlmock.NewResult(0, 1)) 169 | 170 | err := repo.Delete(u.ID) 171 | assert.NoError(t, err) 172 | } 173 | 174 | func TestDeleteError(t *testing.T) { 175 | db, mock := NewMock() 176 | repo := &repository{db} 177 | defer func() { 178 | repo.Close() 179 | }() 180 | 181 | query := "DELETE FROM user WHERE id = \\?" 182 | 183 | prep := mock.ExpectPrepare(query) 184 | prep.ExpectExec().WithArgs(u.ID).WillReturnResult(sqlmock.NewResult(0, 0)) 185 | 186 | err := repo.Delete(u.ID) 187 | assert.Error(t, err) 188 | } 189 | -------------------------------------------------------------------------------- /repository/postgres/postgres_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // go-unit-test-sql 3 | // 4 | // Copyright © 2020. All rights reserved. 5 | // 6 | 7 | package postgres 8 | 9 | import ( 10 | "database/sql" 11 | "log" 12 | "testing" 13 | 14 | r "github.com/moemoe89/go-unit-test-sql/repository" 15 | 16 | "github.com/DATA-DOG/go-sqlmock" 17 | "github.com/google/uuid" 18 | "github.com/stretchr/testify/assert" 19 | ) 20 | 21 | var u = &r.UserModel{ 22 | ID: uuid.New().String(), 23 | Name: "Momo", 24 | Email: "momo@mail.com", 25 | Phone: "08123456789", 26 | } 27 | 28 | func NewMock() (*sql.DB, sqlmock.Sqlmock) { 29 | db, mock, err := sqlmock.New() 30 | if err != nil { 31 | log.Fatalf("an error '%s' was not expected when opening a stub database connection", err) 32 | } 33 | 34 | return db, mock 35 | } 36 | 37 | func TestFindByID(t *testing.T) { 38 | db, mock := NewMock() 39 | repo := &repository{db} 40 | defer func() { 41 | repo.Close() 42 | }() 43 | 44 | query := "SELECT id, name, email, phone FROM users WHERE id = \\$1" 45 | 46 | rows := sqlmock.NewRows([]string{"id", "name", "email", "phone"}). 47 | AddRow(u.ID, u.Name, u.Email, u.Phone) 48 | 49 | mock.ExpectQuery(query).WithArgs(u.ID).WillReturnRows(rows) 50 | 51 | user, err := repo.FindByID(u.ID) 52 | assert.NotNil(t, user) 53 | assert.NoError(t, err) 54 | } 55 | 56 | func TestFindByIDError(t *testing.T) { 57 | db, mock := NewMock() 58 | repo := &repository{db} 59 | defer func() { 60 | repo.Close() 61 | }() 62 | 63 | query := "SELECT id, name, email, phone FROM user WHERE id = \\$1" 64 | 65 | rows := sqlmock.NewRows([]string{"id", "name", "email", "phone"}) 66 | 67 | mock.ExpectQuery(query).WithArgs(u.ID).WillReturnRows(rows) 68 | 69 | user, err := repo.FindByID(u.ID) 70 | assert.Empty(t, user) 71 | assert.Error(t, err) 72 | } 73 | 74 | func TestFind(t *testing.T) { 75 | db, mock := NewMock() 76 | repo := &repository{db} 77 | defer func() { 78 | repo.Close() 79 | }() 80 | 81 | query := "SELECT id, name, email, phone FROM users" 82 | 83 | rows := sqlmock.NewRows([]string{"id", "name", "email", "phone"}). 84 | AddRow(u.ID, u.Name, u.Email, u.Phone) 85 | 86 | mock.ExpectQuery(query).WillReturnRows(rows) 87 | 88 | users, err := repo.Find() 89 | assert.NotEmpty(t, users) 90 | assert.NoError(t, err) 91 | assert.Len(t, users, 1) 92 | } 93 | 94 | func TestCreate(t *testing.T) { 95 | db, mock := NewMock() 96 | repo := &repository{db} 97 | defer func() { 98 | repo.Close() 99 | }() 100 | 101 | query := "INSERT INTO users \\(id, name, email, phone\\) VALUES \\(\\$1, \\$2, \\$3, \\$4\\)" 102 | 103 | prep := mock.ExpectPrepare(query) 104 | prep.ExpectExec().WithArgs(u.ID, u.Name, u.Email, u.Phone).WillReturnResult(sqlmock.NewResult(0, 1)) 105 | 106 | err := repo.Create(u) 107 | assert.NoError(t, err) 108 | } 109 | 110 | func TestCreateError(t *testing.T) { 111 | db, mock := NewMock() 112 | repo := &repository{db} 113 | defer func() { 114 | repo.Close() 115 | }() 116 | 117 | query := "INSERT INTO user \\(id, name, email, phone\\) VALUES \\(\\$1, \\$2, \\$3, \\$4\\)" 118 | 119 | prep := mock.ExpectPrepare(query) 120 | prep.ExpectExec().WithArgs(u.ID, u.Name, u.Email, u.Phone).WillReturnResult(sqlmock.NewResult(0, 0)) 121 | 122 | err := repo.Create(u) 123 | assert.Error(t, err) 124 | } 125 | 126 | func TestUpdate(t *testing.T) { 127 | db, mock := NewMock() 128 | repo := &repository{db} 129 | defer func() { 130 | repo.Close() 131 | }() 132 | 133 | query := "UPDATE users SET name = \\$1, email = \\$2, phone = \\$3 WHERE id = \\$4" 134 | 135 | prep := mock.ExpectPrepare(query) 136 | prep.ExpectExec().WithArgs(u.Name, u.Email, u.Phone, u.ID).WillReturnResult(sqlmock.NewResult(0, 1)) 137 | 138 | err := repo.Update(u) 139 | assert.NoError(t, err) 140 | } 141 | 142 | func TestUpdateErr(t *testing.T) { 143 | db, mock := NewMock() 144 | repo := &repository{db} 145 | defer func() { 146 | repo.Close() 147 | }() 148 | 149 | query := "UPDATE user SET name = \\$1, email = \\$2, phone = \\$3 WHERE id = \\$4" 150 | 151 | prep := mock.ExpectPrepare(query) 152 | prep.ExpectExec().WithArgs(u.Name, u.Email, u.Phone, u.ID).WillReturnResult(sqlmock.NewResult(0, 0)) 153 | 154 | err := repo.Update(u) 155 | assert.Error(t, err) 156 | } 157 | 158 | func TestDelete(t *testing.T) { 159 | db, mock := NewMock() 160 | repo := &repository{db} 161 | defer func() { 162 | repo.Close() 163 | }() 164 | 165 | query := "DELETE FROM users WHERE id = \\$1" 166 | 167 | prep := mock.ExpectPrepare(query) 168 | prep.ExpectExec().WithArgs(u.ID).WillReturnResult(sqlmock.NewResult(0, 1)) 169 | 170 | err := repo.Delete(u.ID) 171 | assert.NoError(t, err) 172 | } 173 | 174 | func TestDeleteError(t *testing.T) { 175 | db, mock := NewMock() 176 | repo := &repository{db} 177 | defer func() { 178 | repo.Close() 179 | }() 180 | 181 | query := "DELETE FROM user WHERE id = \\$1" 182 | 183 | prep := mock.ExpectPrepare(query) 184 | prep.ExpectExec().WithArgs(u.ID).WillReturnResult(sqlmock.NewResult(0, 0)) 185 | 186 | err := repo.Delete(u.ID) 187 | assert.Error(t, err) 188 | } 189 | --------------------------------------------------------------------------------