├── 01-normal ├── normal.go └── normal_test.go ├── 02-table ├── table.go └── table_test.go ├── 03-assertion ├── assert.go └── assert_test.go ├── 04-testmain └── testMain_test.go ├── 05-parallel ├── paralel.go └── paralel_test.go ├── 06-subtest └── subtest_test.go ├── 07-nethttp ├── main.go └── main_test.go ├── 08-ginhttp ├── main.go └── main_test.go ├── 09-database ├── exampledb.sql ├── main.go └── main_test.go ├── 10-mocking ├── mocking.go └── mocking_test.go └── README.md /01-normal/normal.go: -------------------------------------------------------------------------------- 1 | package normal 2 | 3 | func Sum(x, y int) int { 4 | z := x + y 5 | return z 6 | } 7 | -------------------------------------------------------------------------------- /01-normal/normal_test.go: -------------------------------------------------------------------------------- 1 | package normal 2 | 3 | import "testing" 4 | 5 | func TestSum(t *testing.T) { 6 | 7 | want := 8 8 | got := Sum(3, 5) 9 | 10 | if got != want { 11 | t.Errorf("Test fail! want: '%d', got: '%d'", want, got) 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /02-table/table.go: -------------------------------------------------------------------------------- 1 | package table 2 | 3 | func Sum(x, y int) int { 4 | z := x + y 5 | return z 6 | } 7 | -------------------------------------------------------------------------------- /02-table/table_test.go: -------------------------------------------------------------------------------- 1 | package table 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | var table = []struct { 9 | x int 10 | y int 11 | want int 12 | }{ 13 | {2, 2, 4}, 14 | {5, 3, 8}, 15 | {8, 4, 12}, 16 | {12, 5, 17}, 17 | } 18 | 19 | //Table Test (Normal) 20 | func TestSum(t *testing.T) { 21 | for _, row := range table { 22 | got := Sum(row.x, row.y) 23 | if got != row.want { 24 | t.Errorf("Test fail! want: '%d', got: '%d'", row.want, got) 25 | } 26 | } 27 | } 28 | 29 | //Table Test (With Subtest) 30 | func TestSumSubtest(t *testing.T) { 31 | for _, row := range table { 32 | testName := fmt.Sprintf("Test %d+%d", row.x, row.y) 33 | t.Run(testName, func(t *testing.T) { 34 | got := Sum(row.x, row.y) 35 | if got != row.want { 36 | t.Errorf("Test fail! want: '%d', got: '%d'", row.want, got) 37 | } 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /03-assertion/assert.go: -------------------------------------------------------------------------------- 1 | package assertion 2 | 3 | func Sum(x, y int) (int, error) { 4 | z := x + y 5 | return z, nil 6 | } 7 | -------------------------------------------------------------------------------- /03-assertion/assert_test.go: -------------------------------------------------------------------------------- 1 | package assertion 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | // Assertion example with testify 11 | func TestSum(t *testing.T) { 12 | 13 | // Use this for more then one assert 14 | // assert := assert.New(t) 15 | 16 | got, err := Sum(2, 5) 17 | 18 | assert.Equal(t, 7, got, "they should be equal") 19 | 20 | assert.NotEqual(t, 6, got, "they should not be equal") 21 | 22 | assert.Nil(t, err) 23 | 24 | err = errors.New("different then nil") 25 | if assert.NotNil(t, err) { 26 | // now we know that object isn't nil, we are safe to make 27 | // further assertions without causing any errors 28 | assert.Equal(t, "different then nil", err.Error()) 29 | } 30 | } 31 | 32 | // Assertion with table test 33 | func TestSumTable(t *testing.T) { 34 | assert := assert.New(t) 35 | 36 | var table = []struct { 37 | x int 38 | y int 39 | want int 40 | }{ 41 | {2, 2, 4}, 42 | {5, 3, 8}, 43 | {8, 4, 12}, 44 | {12, 5, 17}, 45 | } 46 | 47 | for _, test := range table { 48 | got, _ := Sum(test.x, test.y) 49 | assert.Equal(test.want, got) 50 | } 51 | } 52 | 53 | // Custom assertion function 54 | func TestSumCustomAssertion(t *testing.T) { 55 | 56 | customAssert := func(t *testing.T, got, want int) { 57 | t.Helper() 58 | if got != want { 59 | t.Errorf("got %q want %q", got, want) 60 | } 61 | } 62 | 63 | t.Run("Test 1+3", func(t *testing.T) { 64 | got, _ := Sum(1, 3) 65 | want := 4 66 | customAssert(t, got, want) 67 | }) 68 | 69 | t.Run("Test 4+7", func(t *testing.T) { 70 | got, _ := Sum(4, 7) 71 | want := 11 72 | customAssert(t, got, want) 73 | }) 74 | 75 | } 76 | -------------------------------------------------------------------------------- /04-testmain/testMain_test.go: -------------------------------------------------------------------------------- 1 | package testMain 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | func TestA(t *testing.T) { 9 | t.Log("Test A run") 10 | } 11 | 12 | func TestB(t *testing.T) { 13 | t.Log("Test B run") 14 | } 15 | 16 | func TestMain(m *testing.M) { 17 | // setup() 18 | exitVal := m.Run() 19 | if exitVal == 0 { 20 | // teardown() 21 | } 22 | os.Exit(exitVal) 23 | } 24 | -------------------------------------------------------------------------------- /05-parallel/paralel.go: -------------------------------------------------------------------------------- 1 | package parallel 2 | 3 | func Sum(x, y int) (int, error) { 4 | z := x + y 5 | return z, nil 6 | } 7 | -------------------------------------------------------------------------------- /05-parallel/paralel_test.go: -------------------------------------------------------------------------------- 1 | package parallel 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | var table = []struct { 9 | x int 10 | y int 11 | want int 12 | }{ 13 | {2, 2, 4}, 14 | {5, 3, 8}, 15 | {8, 4, 12}, 16 | {12, 5, 17}, 17 | } 18 | 19 | func TestSumParalel(t *testing.T) { 20 | t.Parallel() 21 | for _, row := range table { 22 | testName := fmt.Sprintf("Test %d+%d", row.x, row.y) 23 | t.Run(testName, func(t *testing.T) { 24 | t.Parallel() 25 | got, _ := Sum(row.x, row.y) 26 | if got != row.want { 27 | t.Errorf("Test fail! want: '%d', got: '%d'", row.want, got) 28 | } 29 | }) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /06-subtest/subtest_test.go: -------------------------------------------------------------------------------- 1 | package subtest 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestSubtests(t *testing.T) { 8 | // 9 | t.Run("A", func(t *testing.T) { 10 | t.Log("Test A1 completed") 11 | }) 12 | 13 | t.Run("B", func(t *testing.T) { 14 | t.Log("Test B completed") 15 | }) 16 | 17 | t.Run("C", func(t *testing.T) { 18 | t.Log("Test C completed") 19 | }) 20 | // 21 | } 22 | -------------------------------------------------------------------------------- /07-nethttp/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | func main() { 9 | http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { 10 | fmt.Fprintf(w, "pong") 11 | }) 12 | 13 | http.ListenAndServe(":8090", nil) 14 | } 15 | -------------------------------------------------------------------------------- /07-nethttp/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | "net/http/httptest" 7 | "testing" 8 | ) 9 | 10 | func TestHttp(t *testing.T) { 11 | handler := func(w http.ResponseWriter, r *http.Request) { 12 | io.WriteString(w, "pong") 13 | } 14 | 15 | req := httptest.NewRequest("GET", "/ping", nil) 16 | w := httptest.NewRecorder() 17 | handler(w, req) 18 | 19 | // Status code test 20 | if w.Code != 404 { 21 | t.Error("Http test isteği başarısız") 22 | } 23 | 24 | // Return value test 25 | if w.Body.String() != "pongd" { 26 | t.Error("Dönen cevap farklı, test başarısız") 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /08-ginhttp/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func setupRouter() *gin.Engine { 6 | r := gin.Default() 7 | r.GET("/ping", func(c *gin.Context) { 8 | c.String(200, "pong") 9 | }) 10 | return r 11 | } 12 | 13 | func main() { 14 | r := setupRouter() 15 | r.Run(":8091") 16 | } 17 | -------------------------------------------------------------------------------- /08-ginhttp/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | ) 8 | 9 | func TestPingRoute(t *testing.T) { 10 | router := setupRouter() 11 | 12 | w := httptest.NewRecorder() 13 | req, _ := http.NewRequest("GET", "/ping", nil) 14 | router.ServeHTTP(w, req) 15 | 16 | // Status code test 17 | if w.Code != 202 { 18 | t.Error("Http test isteği başarısız") 19 | } 20 | 21 | // Return value test 22 | if w.Body.String() != "pongs" { 23 | t.Error("Dönen cevap farklı, test başarısız") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /09-database/exampledb.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE gotest; 2 | 3 | CREATE TABLE IF NOT EXISTS tasks ( 4 | id INT AUTO_INCREMENT PRIMARY KEY, 5 | title VARCHAR(255) NOT NULL, 6 | start_date DATE, 7 | due_date DATE, 8 | status TINYINT NOT NULL, 9 | priority TINYINT NOT NULL, 10 | description TEXT, 11 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 12 | ) ENGINE=INNODB; 13 | 14 | INSERT INTO tasks (title,start_date,due_date,status,priority,description,created_at) VALUES ('Go ile Test Yazmayı Öğren',now(),now() + interval 1 month,1,1,"Test yazmak gerçekten çok önemlidir. Kodu kalitesini artırır. Hataları giderir.",now()); -------------------------------------------------------------------------------- /09-database/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "errors" 6 | "fmt" 7 | "log" 8 | "time" 9 | 10 | _ "github.com/go-sql-driver/mysql" 11 | ) 12 | 13 | var ( 14 | err error 15 | ) 16 | 17 | type Task struct { 18 | ID int 19 | Title string 20 | StartDate time.Time 21 | DueDate time.Time 22 | Status bool 23 | Priority bool 24 | Description string 25 | CreatedAt time.Time 26 | } 27 | 28 | func dbConn() (db *sql.DB) { 29 | dbDriver := "mysql" 30 | dbUser := "root" 31 | dbPass := "12345" 32 | dbName := "gotest" 33 | db, err := sql.Open(dbDriver, dbUser+":"+dbPass+"@/"+dbName+"?parseTime=true") 34 | if err != nil { 35 | panic(err.Error()) 36 | } 37 | return db 38 | } 39 | 40 | type Repository interface { 41 | Find(id int) (Task, error) 42 | Add(task Task) (int64, error) 43 | } 44 | 45 | type repository struct { 46 | db *sql.DB 47 | } 48 | 49 | func NewRepository(db *sql.DB) Repository { 50 | return &repository{db: dbConn()} 51 | } 52 | 53 | func (r repository) Find(id int) (Task, error) { 54 | var task Task 55 | 56 | rows, _ := r.db.Query("SELECT * FROM tasks WHERE id=?", id) 57 | defer rows.Close() 58 | 59 | // Eğer kayıt yoksa 60 | if rows.Next() == false { 61 | return task, errors.New("Kayıt bulunamadı!") 62 | } 63 | 64 | for rows.Next() { 65 | err := rows.Scan(&task.ID, &task.Title, &task.StartDate, &task.DueDate, &task.Status, &task.Priority, &task.Description, &task.CreatedAt) 66 | if err != nil { 67 | return task, errors.New("Kayıtlar eşleştirilemedi!") 68 | } 69 | } 70 | 71 | if err = rows.Err(); err != nil { 72 | return task, err 73 | } 74 | 75 | return task, nil 76 | } 77 | 78 | func (r repository) Add(task Task) (int64, error) { 79 | 80 | stmt, err := r.db.Prepare("INSERT INTO tasks (title,start_date,due_date,status,priority,description,created_at) VALUES(?,?,?,?,?,?,?)") 81 | if err != nil { 82 | log.Fatal(err) 83 | } 84 | 85 | res, err := stmt.Exec(task.Title, task.StartDate, task.DueDate, task.Status, task.Priority, task.Description, task.CreatedAt) 86 | if err != nil { 87 | return 0, err 88 | } 89 | 90 | lastID, err := res.LastInsertId() 91 | if err != nil { 92 | return 0, err 93 | } 94 | 95 | return lastID, nil 96 | } 97 | 98 | func main() { 99 | db := dbConn() 100 | myDB := NewRepository(db) 101 | // var task Task 102 | 103 | // 1 nolu kayıdı bul 104 | /* if task, err = myDB.Find(1); err != nil { 105 | log.Println(err) 106 | return 107 | } 108 | 109 | jsonResult, _ := json.MarshalIndent(task, "", " ") 110 | fmt.Println(string(jsonResult)) 111 | */ 112 | 113 | task := Task{ 114 | Title: "React Native Öğren", 115 | StartDate: time.Now(), 116 | DueDate: time.Now(), 117 | Status: true, 118 | Priority: true, 119 | Description: "Mobil uygulama geliştirme artık günümüzün olmazsa olmazı.", 120 | CreatedAt: time.Now(), 121 | } 122 | 123 | lastID, err := myDB.Add(task) 124 | if err != nil { 125 | log.Println(err) 126 | return 127 | } 128 | 129 | fmt.Println(lastID) 130 | 131 | } 132 | -------------------------------------------------------------------------------- /09-database/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "testing" 7 | "time" 8 | 9 | "github.com/DATA-DOG/go-sqlmock" 10 | ) 11 | 12 | func TestFind(t *testing.T) { 13 | // the db satisfy the sql.DB struct 14 | db, mock, err := sqlmock.New() 15 | if err != nil { 16 | t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) 17 | } 18 | defer db.Close() 19 | 20 | // our sql.DB must exec the follow query 21 | mock.ExpectQuery("SELECT * FROM tasks"). 22 | // the number 3 must be on the query like "where id = 3" 23 | WithArgs(1) 24 | // TODO: define the values that need to be returned from the database 25 | 26 | myDB := NewRepository(db) // passes the mock to our code 27 | 28 | // run the code with the database mock 29 | if _, err := myDB.Find(1); err != nil { 30 | t.Errorf("something went wrong: %s", err.Error()) 31 | } 32 | } 33 | 34 | func TestAdd(t *testing.T) { 35 | // the db satisfy the sql.DB struct 36 | db, mock, err := sqlmock.New() 37 | if err != nil { 38 | t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) 39 | } 40 | defer db.Close() 41 | 42 | var id int64 43 | task := Task{ 44 | Title: "React Native Öğren", 45 | StartDate: time.Now(), 46 | DueDate: time.Now(), 47 | Status: true, 48 | Priority: true, 49 | Description: "Mobil uygulama geliştirme artık günümüzün olmazsa olmazı.", 50 | CreatedAt: time.Now(), 51 | } 52 | 53 | mock.ExpectQuery(regexp.QuoteMeta( 54 | `INSERT INTO tasks (title,start_date,due_date,status,priority,description,created_at) VALUES($1,$2,$3,$4,$5,$6,$7)`)). 55 | WithArgs(task.Title, task.StartDate, task.DueDate, task.Status, task.Priority, task.Description, task.CreatedAt). 56 | WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(id)) 57 | 58 | myDB := NewRepository(db) // passes the mock to our code 59 | 60 | lastID, err := myDB.Add(task) 61 | if err != nil { 62 | t.Errorf("something went wrong: %s", err.Error()) 63 | } 64 | fmt.Println(lastID) 65 | } 66 | -------------------------------------------------------------------------------- /10-mocking/mocking.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | /* Gerçek Fonksiyonlar ********** */ 11 | type Matematik interface { 12 | MockTopla([]int) (int, error) 13 | } 14 | 15 | type islem struct{} 16 | 17 | func (*islem) MockTopla(sayilar []int) (int, error) { 18 | toplam := 0 19 | for i := range sayilar { 20 | toplam = toplam + sayilar[i] 21 | } 22 | return toplam, nil 23 | } 24 | 25 | /* Test işlemleri ********** */ 26 | type MockRepository struct { 27 | mock.Mock 28 | } 29 | 30 | func (mock *MockRepository) MockTopla(sayilar []int) (int, error) { 31 | // Called, mock objesine bir methodun çağırıldığını haver verir ve döndürmek üzere bir dizi (array) değer alır 32 | args := mock.Called(sayilar) 33 | result := args.Get(0) 34 | 35 | return result.(int), args.Error(1) 36 | // Veya 37 | // return args.Int(0), args.Error(1) 38 | // Diğer seçenekler: args.Int(0) args.Bool(1) args.String(2) 39 | } 40 | 41 | func TestMockTopla(t *testing.T) { 42 | 43 | // Test objemizin bir kopyasını oluşturalım 44 | mockRepo := new(MockRepository) 45 | 46 | // setup expectations 47 | mockRepo.On("MockTopla", []int{2, 3}).Return(5, nil) 48 | 49 | // Testini yaptığımız kodu çağıralım 50 | testMatematik := Matematik(mockRepo) 51 | 52 | sonuc, err := testMatematik.MockTopla([]int{2, 3}) 53 | 54 | // Beklentilerin karşılandığını iddia et (assert) 55 | mockRepo.AssertExpectations(t) 56 | 57 | assert.Equal(t, 5, sonuc) 58 | assert.Nil(t, err) 59 | } 60 | -------------------------------------------------------------------------------- /10-mocking/mocking_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | /* Gerçek Fonksiyonlar ********** */ 11 | type Matematik interface { 12 | MockTopla([]int) (int, error) 13 | } 14 | 15 | type islem struct{} 16 | 17 | func (*islem) MockTopla(sayilar []int) (int, error) { 18 | toplam := 0 19 | for i := range sayilar { 20 | toplam = toplam + sayilar[i] 21 | } 22 | return toplam, nil 23 | } 24 | 25 | /* Test işlemleri ********** */ 26 | type MockRepository struct { 27 | mock.Mock 28 | } 29 | 30 | func (mock *MockRepository) MockTopla(sayilar []int) (int, error) { 31 | // Called, mock objesine bir methodun çağırıldığını haver verir ve döndürmek üzere bir dizi (array) değer alır 32 | args := mock.Called(sayilar) 33 | result := args.Get(0) 34 | 35 | return result.(int), args.Error(1) 36 | // Veya 37 | // return args.Int(0), args.Error(1) 38 | // Diğer seçenekler: args.Int(0) args.Bool(1) args.String(2) 39 | } 40 | 41 | func TestMockTopla(t *testing.T) { 42 | 43 | // Test objemizin bir kopyasını oluşturalım 44 | mockRepo := new(MockRepository) 45 | 46 | // setup expectations 47 | mockRepo.On("MockTopla", []int{2, 3}).Return(5, nil) 48 | 49 | // Testini yaptığımız kodu çağıralım 50 | testMatematik := Matematik(mockRepo) 51 | 52 | sonuc, err := testMatematik.MockTopla([]int{2, 3}) 53 | 54 | // Beklentilerin karşılandığını iddia et (assert) 55 | mockRepo.AssertExpectations(t) 56 | 57 | assert.Equal(t, 5, sonuc) 58 | assert.Nil(t, err) 59 | } 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-test-examples 2 | This repo conains different kinds of golang test methods. I hope that will be useful. 3 | 4 | ## Subjects: 5 | 1. Normal test 6 | 2. Table design test 7 | 3. Assertion logic 8 | 4. TestMain function 9 | 5. Parallel testing 10 | 6. Subtest logic 11 | 7. Testing API endpoints with httptest 12 | 8. Testing gin framework API endpoints with httptest 13 | 9. Database test 14 | 10. Mocking 15 | 11. Code coverage --------------------------------------------------------------------------------