11 Tambah Unit Test Menggunakan Mockery
Pada tahapan ini kita akan membutuhkan dependency sebagai berikut
1brew install mockery1go get github.com/stretchr/testify1go mod tidy1go mod vendor1go get github.com/DATA-DOG/go-sqlmockPada projek kali ini kita akan mencoba membuat unit test menggunakan library mocking dengan nama mockery. Library ini banyak dipakai oleh kalangan developer golang karena kemudahan penggunaannya dan memiliki kelebihan fitur-fitur yang bisa mengcover semua unit test yang diperlukan. Jika teman-teman pernah menggunakan unit test pada golang mungkin sudah tahu library ini tetapi jika teman-teman ingin lebih mendapatkan referensi yang lebih banyak, Santekno juga telah memberikan postingan sebelumnya diantaranya:
- Cara Membuat Unit Test Pada Golang
- Cara Membuat Unit Benchmark Pada Golang
- Cara Membuat Unit Test Menggunakan Library Moq Pada Golang
- Teknik Membuat Mocking Unit Test Pada Golang
- Cara Membuat Integration Test Pada Golang
- Atau kumpulan artikel mengenai unit test
Unit Test Repository
Pada pertama kali kita akan membuat unit test pada level repository. Repository ini terdapat beberapa fungsi sehingga nanti akan membuat unit test satu persatu agar lebih detail. Lalu sebelum membuat unit test kita perlu membuat mocking untuk dipakai nanti di unit test level atasnya misalkan repository akan butuhkan pada level usecase maka kita perlu melakukan generate dan menambahkan kode dibawah ini pada interface yang telah di definisikan.
Buka file reposito.go dan tambahkan kode seperti dibawah ini.
1//go:generate mockery --name=ArticleRepository --filename=repository_mock.go --inpackage
2
3type ArticleRepository interface {
4 GetAll(ctx context.Context) ([]*models.Article, error)
5 GetByID(ctx context.Context, id int64) (*models.Article, error)
6 Update(ctx context.Context, article *models.Article) (*models.Article, error)
7 Store(ctx context.Context, article *models.Article) (int64, error)
8 Delete(ctx context.Context, id int64) (bool, error)
9}1go generate ./...Jika sukses akan terdapat file repository_mock.go yang telah otomatis ter-generate sendiri.
Membuat unit test initiation
Berikut ini unit test untuk inisiasi repository.
1func TestNew(t *testing.T) {
2 type args struct {
3 conn *sql.DB
4 }
5 tests := []struct {
6 name string
7 args args
8 want *ArticleStore
9 }{
10 {
11 name: "success",
12 args: args{},
13 want: &ArticleStore{},
14 },
15 }
16 for _, tt := range tests {
17 t.Run(tt.name, func(t *testing.T) {
18 if got := New(tt.args.conn); !reflect.DeepEqual(got, tt.want) {
19 t.Errorf("New() = %v, want %v", got, tt.want)
20 }
21 })
22 }
23}Membuat unit test pada Fungsi GetAll
1func TestArticleStore_GetAll(t *testing.T) {
2 ctx := context.Background()
3 mockDB, mock, err := sqlmock.New()
4 if err != nil {
5 t.Errorf("error")
6 }
7
8 // expect query
9 const expetcqueryGetAll = `SELECT id, title, content, create_at, update_at FROM articles`
10 var column = []string{"id", "title", "content", "create_at", "update_at"}
11 var timeNow = time.Now()
12 var expectResult []*models.Article
13
14 type fields struct {
15 db *sql.DB
16 }
17 type args struct {
18 ctx context.Context
19 }
20 tests := []struct {
21 name string
22 fields fields
23 args args
24 want []*models.Article
25 wantErr bool
26 mock func()
27 }{
28 {
29 name: "success get article",
30 fields: fields{
31 db: mockDB,
32 },
33 mock: func() {
34 mock.ExpectQuery(expetcqueryGetAll).WillReturnRows(
35 sqlmock.NewRows(column).AddRow(
36 1, "test", "test content", timeNow, timeNow,
37 ),
38 )
39 },
40 want: []*models.Article{
41 {
42 ID: 1,
43 Title: "test",
44 Content: "test content",
45 CreateAt: timeNow,
46 UpdateAt: timeNow,
47 },
48 },
49 args: args{
50 ctx: ctx,
51 },
52 wantErr: false,
53 },
54 {
55 name: "failed when query error",
56 fields: fields{
57 db: mockDB,
58 },
59 mock: func() {
60 mock.ExpectQuery(expetcqueryGetAll).WillReturnError(sql.ErrNoRows)
61 },
62 want: expectResult,
63 args: args{
64 ctx: ctx,
65 },
66 wantErr: true,
67 },
68 {
69 name: "failed rows next",
70 fields: fields{
71 db: mockDB,
72 },
73 mock: func() {
74 mock.ExpectQuery(expetcqueryGetAll).WillReturnRows(
75 sqlmock.NewRows(column).AddRow(
76 1, "test", "test content", "test", "test",
77 ),
78 )
79 },
80 want: expectResult,
81 args: args{
82 ctx: ctx,
83 },
84 wantErr: true,
85 },
86 }
87 for _, tt := range tests {
88 t.Run(tt.name, func(t *testing.T) {
89 r := &ArticleStore{
90 db: tt.fields.db,
91 }
92 tt.mock()
93 got, err := r.GetAll(tt.args.ctx)
94 if (err != nil) != tt.wantErr {
95 t.Errorf("ArticleStore.GetAll() error = %v, wantErr %v", err, tt.wantErr)
96 return
97 }
98 assert.EqualValues(t, got, tt.want)
99 })
100 }
101}Membuat unit test pada fungsii GetByID
1func TestArticleStore_GetByID(t *testing.T) {
2 ctx := context.Background()
3 mockDB, mock, err := sqlmock.New()
4 if err != nil {
5 t.Errorf("error")
6 }
7
8 // expect query
9 const expectQueryGetById = `SELECT id, title, content, create_at, update_at FROM articles WHERE id=(.*)`
10 var column = []string{"id", "title", "content", "create_at", "update_at"}
11 var timeNow = time.Now()
12
13 type fields struct {
14 db *sql.DB
15 }
16 type args struct {
17 ctx context.Context
18 id int64
19 }
20 tests := []struct {
21 name string
22 fields fields
23 args args
24 want *models.Article
25 wantErr bool
26 mock func()
27 }{
28 {
29 name: "success get article",
30 fields: fields{
31 db: mockDB,
32 },
33 mock: func() {
34 mock.ExpectQuery(expectQueryGetById).WillReturnRows(
35 sqlmock.NewRows(column).AddRow(
36 1, "test", "test content", timeNow, timeNow,
37 ),
38 )
39 },
40 want: &models.Article{
41 ID: 1,
42 Title: "test",
43 Content: "test content",
44 CreateAt: timeNow,
45 UpdateAt: timeNow,
46 },
47 args: args{
48 ctx: ctx,
49 },
50 wantErr: false,
51 },
52 {
53 name: "failed when query error",
54 fields: fields{
55 db: mockDB,
56 },
57 mock: func() {
58 mock.ExpectQuery(expectQueryGetById).WillReturnError(sql.ErrNoRows)
59 },
60 want: &models.Article{},
61 args: args{
62 ctx: ctx,
63 },
64 wantErr: true,
65 },
66 }
67 for _, tt := range tests {
68 t.Run(tt.name, func(t *testing.T) {
69 r := &ArticleStore{
70 db: tt.fields.db,
71 }
72 tt.mock()
73 got, err := r.GetByID(tt.args.ctx, tt.args.id)
74 if (err != nil) != tt.wantErr {
75 t.Errorf("ArticleStore.GetByID() error = %v, wantErr %v", err, tt.wantErr)
76 return
77 }
78 assert.EqualExportedValues(t, got, tt.want)
79 })
80 }
81}Membuat unit test pada fungsi Update
1func TestArticleStore_Update(t *testing.T) {
2 ctx := context.Background()
3 mockDB, mock, err := sqlmock.New()
4 if err != nil {
5 t.Errorf("error")
6 }
7
8 // expect query
9 const expectQueryUpdate = `UPDATE articles SET title=(.*), content=(.*), update_at=(.*) WHERE id=(.*)`
10 var timeNow = time.Now()
11
12 type fields struct {
13 db *sql.DB
14 }
15 type args struct {
16 ctx context.Context
17 article *models.Article
18 }
19 tests := []struct {
20 name string
21 fields fields
22 args args
23 want *models.Article
24 wantErr bool
25 mock func()
26 }{
27 {
28 name: "success update article",
29 fields: fields{
30 db: mockDB,
31 },
32 mock: func() {
33 mock.ExpectExec(expectQueryUpdate).WillReturnResult(
34 sqlmock.NewResult(1, 1),
35 )
36 },
37 want: &models.Article{
38 ID: 1,
39 Title: "test",
40 Content: "test content",
41 CreateAt: timeNow,
42 UpdateAt: timeNow,
43 },
44 args: args{
45 ctx: ctx,
46 article: &models.Article{
47 ID: 1,
48 Title: "test",
49 Content: "test content",
50 CreateAt: timeNow,
51 UpdateAt: timeNow,
52 },
53 },
54 wantErr: false,
55 },
56 {
57 name: "failed when query error",
58 fields: fields{
59 db: mockDB,
60 },
61 mock: func() {
62 mock.ExpectExec(expectQueryUpdate).WillReturnError(errors.New("got error"))
63 },
64 want: nil,
65 args: args{
66 ctx: ctx,
67 article: &models.Article{
68 ID: 1,
69 Title: "test",
70 Content: "test content",
71 CreateAt: timeNow,
72 UpdateAt: timeNow,
73 },
74 },
75 wantErr: true,
76 },
77 }
78 for _, tt := range tests {
79 t.Run(tt.name, func(t *testing.T) {
80 r := &ArticleStore{
81 db: tt.fields.db,
82 }
83 tt.mock()
84 got, err := r.Update(tt.args.ctx, tt.args.article)
85 if (err != nil) != tt.wantErr {
86 t.Errorf("ArticleStore.Update() error = %v, wantErr %v", err, tt.wantErr)
87 return
88 }
89 assert.EqualExportedValues(t, got, tt.want)
90 })
91 }
92}Membuat unit test pada fungsi Store
1func TestArticleStore_Store(t *testing.T) {
2 ctx := context.Background()
3 mockDB, mock, err := sqlmock.New()
4 if err != nil {
5 t.Errorf("error")
6 }
7
8 // expect query
9 const expectQueryInsert = `INSERT INTO articles\(title, content, create_at, update_at\) VALUES\((.*),(.*),(.*),(.*)\)`
10 var timeNow = time.Now()
11
12 type fields struct {
13 db *sql.DB
14 }
15 type args struct {
16 ctx context.Context
17 article *models.Article
18 }
19 tests := []struct {
20 name string
21 fields fields
22 args args
23 want int64
24 wantErr bool
25 mock func()
26 }{
27 {
28 name: "success create article",
29 fields: fields{
30 db: mockDB,
31 },
32 mock: func() {
33 mock.ExpectExec(expectQueryInsert).WillReturnResult(
34 sqlmock.NewResult(1, 1),
35 )
36 },
37 want: 1,
38 args: args{
39 ctx: ctx,
40 article: &models.Article{
41 ID: 1,
42 Title: "test",
43 Content: "test content",
44 CreateAt: timeNow,
45 UpdateAt: timeNow,
46 },
47 },
48 wantErr: false,
49 },
50 {
51 name: "failed when query error",
52 fields: fields{
53 db: mockDB,
54 },
55 mock: func() {
56 mock.ExpectExec(expectQueryInsert).WillReturnError(errors.New("got error"))
57 },
58 want: 0,
59 args: args{
60 ctx: ctx,
61 article: &models.Article{
62 ID: 1,
63 Title: "test",
64 Content: "test content",
65 CreateAt: timeNow,
66 UpdateAt: timeNow,
67 },
68 },
69 wantErr: true,
70 },
71 }
72 for _, tt := range tests {
73 t.Run(tt.name, func(t *testing.T) {
74 r := &ArticleStore{
75 db: tt.fields.db,
76 }
77 tt.mock()
78 got, err := r.Store(tt.args.ctx, tt.args.article)
79 if (err != nil) != tt.wantErr {
80 t.Errorf("ArticleStore.Store() error = %v, wantErr %v", err, tt.wantErr)
81 return
82 }
83 assert.Equal(t, got, tt.want)
84 })
85 }
86}Membuat unit test pada fungsi Delete
1func TestArticleStore_Delete(t *testing.T) {
2 ctx := context.Background()
3 mockDB, mock, err := sqlmock.New()
4 if err != nil {
5 t.Errorf("error")
6 }
7
8 // expect query
9 const expectQueryDelete = `DELETE FROM articles WHERE id=(.*)`
10 type fields struct {
11 db *sql.DB
12 }
13 type args struct {
14 ctx context.Context
15 id int64
16 }
17 tests := []struct {
18 name string
19 fields fields
20 args args
21 want bool
22 wantErr bool
23 mock func()
24 }{
25 {
26 name: "success delete article",
27 fields: fields{
28 db: mockDB,
29 },
30 mock: func() {
31 mock.ExpectExec(expectQueryDelete).WillReturnResult(
32 sqlmock.NewResult(1, 1),
33 )
34 },
35 want: true,
36 args: args{
37 ctx: ctx,
38 id: 1,
39 },
40 wantErr: false,
41 },
42 {
43 name: "failed when query error",
44 fields: fields{
45 db: mockDB,
46 },
47 mock: func() {
48 mock.ExpectExec(expectQueryDelete).WillReturnError(errors.New("got error"))
49 },
50 want: false,
51 args: args{
52 ctx: ctx,
53 id: 1,
54 },
55 wantErr: true,
56 },
57 }
58 for _, tt := range tests {
59 t.Run(tt.name, func(t *testing.T) {
60 r := &ArticleStore{
61 db: tt.fields.db,
62 }
63 tt.mock()
64 got, err := r.Delete(tt.args.ctx, tt.args.id)
65 if (err != nil) != tt.wantErr {
66 t.Errorf("ArticleStore.Delete() error = %v, wantErr %v", err, tt.wantErr)
67 return
68 }
69 assert.Equal(t, got, tt.want)
70 })
71 }
72}Membuat unit test level Usecase
Pada pembuatan unit test level Usecase ini kita juga perlu menambahkan mock agar bisa dilakukan unit test pada level atasnya yaitu handler.
1//go:generate mockery --name=ArticleUsecase --filename=usecase_mock.go --inpackage
2
3type ArticleUsecase interface {
4 GetAll(ctx context.Context) ([]models.ArticleResponse, error)
5 GetByID(ctx context.Context, id int64) (models.ArticleResponse, error)
6 Update(ctx context.Context, article models.ArticleUpdateRequest) (models.ArticleResponse, error)
7 Store(ctx context.Context, article models.ArticleCreateRequest) (models.ArticleResponse, error)
8 Delete(ctx context.Context, id int64) (bool, error)
9}1go generate ./...lalu akan terbuat file bernama usecase_mock.go.
Pertama buat unit test pada inisialisasi seperti dibawah ini.
1func TestNew(t *testing.T) {
2 type args struct {
3 repo repository.ArticleRepository
4 }
5 tests := []struct {
6 name string
7 args args
8 want *Usecase
9 }{
10 {
11 name: "success new",
12 want: &Usecase{},
13 },
14 }
15 for _, tt := range tests {
16 t.Run(tt.name, func(t *testing.T) {
17 if got := New(tt.args.repo); !reflect.DeepEqual(got, tt.want) {
18 t.Errorf("New() = %v, want %v", got, tt.want)
19 }
20 })
21 }
22}Membuat unit test fungsi GetAll
1func TestUsecase_GetAll(t *testing.T) {
2 var ctx = context.Background()
3 var timeNow = time.Now()
4 mockRepository := new(repository.MockArticleRepository)
5 type fields struct {
6 articleRepository repository.ArticleRepository
7 }
8 type args struct {
9 ctx context.Context
10 }
11 tests := []struct {
12 name string
13 fields fields
14 args args
15 want []models.ArticleResponse
16 wantErr bool
17 mock func(args args)
18 }{
19 {
20 name: "success",
21 args: args{
22 ctx: ctx,
23 },
24 fields: fields{
25 articleRepository: mockRepository,
26 },
27 mock: func(args args) {
28 var res = []*models.Article{
29 {
30 ID: 1,
31 Title: "test",
32 Content: "test content",
33 CreateAt: timeNow,
34 UpdateAt: timeNow,
35 },
36 }
37 mockRepository.On("GetAll", args.ctx).Return(res, nil).Once()
38 },
39 want: []models.ArticleResponse{
40 {
41 ID: 1,
42 Title: "test",
43 Content: "test content",
44 CreateAt: timeNow,
45 UpdateAt: timeNow,
46 },
47 },
48 wantErr: false,
49 },
50 {
51 name: "failed get all data",
52 args: args{
53 ctx: ctx,
54 },
55 fields: fields{
56 articleRepository: mockRepository,
57 },
58 mock: func(args args) {
59 var res = []*models.Article{}
60 mockRepository.On("GetAll", args.ctx).Return(res, errors.New("got error")).Once()
61 },
62 want: nil,
63 wantErr: true,
64 },
65 }
66 for _, tt := range tests {
67 t.Run(tt.name, func(t *testing.T) {
68 tt.mock(tt.args)
69 u := &Usecase{
70 articleRepository: tt.fields.articleRepository,
71 }
72 got, err := u.GetAll(tt.args.ctx)
73 if (err != nil) != tt.wantErr {
74 t.Errorf("Usecase.GetAll() error = %v, wantErr %v", err, tt.wantErr)
75 return
76 }
77 if !reflect.DeepEqual(got, tt.want) {
78 t.Errorf("Usecase.GetAll() = %v, want %v", got, tt.want)
79 }
80 })
81 }
82}Membuat unit test fungsi GetByID
1func TestUsecase_GetByID(t *testing.T) {
2 var ctx = context.Background()
3 var timeNow = time.Now()
4 mockRepository := new(repository.MockArticleRepository)
5 type fields struct {
6 articleRepository repository.ArticleRepository
7 }
8 type args struct {
9 ctx context.Context
10 id int64
11 }
12 tests := []struct {
13 name string
14 fields fields
15 args args
16 want models.ArticleResponse
17 wantErr bool
18 mock func(args args)
19 }{
20 {
21 name: "success",
22 args: args{
23 ctx: ctx,
24 id: 1,
25 },
26 fields: fields{
27 articleRepository: mockRepository,
28 },
29 mock: func(args args) {
30 var res = &models.Article{
31 ID: 1,
32 Title: "test",
33 Content: "test content",
34 CreateAt: timeNow,
35 UpdateAt: timeNow,
36 }
37 mockRepository.On("GetByID", args.ctx, args.id).Return(res, nil).Once()
38 },
39 want: models.ArticleResponse{
40 ID: 1,
41 Title: "test",
42 Content: "test content",
43 CreateAt: timeNow,
44 UpdateAt: timeNow,
45 },
46 wantErr: false,
47 },
48 {
49 name: "failed get by id",
50 args: args{
51 ctx: ctx,
52 },
53 fields: fields{
54 articleRepository: mockRepository,
55 },
56 mock: func(args args) {
57 var res = &models.Article{}
58 mockRepository.On("GetByID", args.ctx, args.id).Return(res, errors.New("got error")).Once()
59 },
60 want: models.ArticleResponse{},
61 wantErr: true,
62 },
63 }
64 for _, tt := range tests {
65 t.Run(tt.name, func(t *testing.T) {
66 tt.mock(tt.args)
67 u := &Usecase{
68 articleRepository: tt.fields.articleRepository,
69 }
70 got, err := u.GetByID(tt.args.ctx, tt.args.id)
71 if (err != nil) != tt.wantErr {
72 t.Errorf("Usecase.GetByID() error = %v, wantErr %v", err, tt.wantErr)
73 return
74 }
75 if !reflect.DeepEqual(got, tt.want) {
76 t.Errorf("Usecase.GetByID() = %v, want %v", got, tt.want)
77 }
78 })
79 }
80}Membuat unit test fungsi Update
1func TestUsecase_Update(t *testing.T) {
2 var ctx = context.Background()
3 var timeNow = time.Now()
4 mockRepository := new(repository.MockArticleRepository)
5 type fields struct {
6 articleRepository repository.ArticleRepository
7 }
8 type args struct {
9 ctx context.Context
10 request models.ArticleUpdateRequest
11 }
12 tests := []struct {
13 name string
14 fields fields
15 args args
16 want models.ArticleResponse
17 wantErr bool
18 mock func(args args)
19 }{
20 {
21 name: "failed when request id was zero",
22 fields: fields{
23 articleRepository: mockRepository,
24 },
25 args: args{
26 ctx: ctx,
27 request: models.ArticleUpdateRequest{},
28 },
29 mock: func(args args) {},
30 want: models.ArticleResponse{},
31 wantErr: true,
32 },
33 {
34 name: "failed when get by repo",
35 fields: fields{
36 articleRepository: mockRepository,
37 },
38 args: args{
39 ctx: ctx,
40 request: models.ArticleUpdateRequest{
41 ID: 1,
42 },
43 },
44 mock: func(args args) {
45 var article *models.Article
46 mockRepository.On("GetByID", args.ctx, args.request.ID).Return(article, errors.New("got error")).Once()
47 },
48 want: models.ArticleResponse{},
49 wantErr: true,
50 },
51 {
52 name: "failed when get by id not found",
53 fields: fields{
54 articleRepository: mockRepository,
55 },
56 args: args{
57 ctx: ctx,
58 request: models.ArticleUpdateRequest{
59 ID: 1,
60 },
61 },
62 mock: func(args args) {
63 var article = &models.Article{}
64 mockRepository.On("GetByID", args.ctx, args.request.ID).Return(article, nil).Once()
65 },
66 want: models.ArticleResponse{},
67 wantErr: true,
68 },
69 {
70 name: "failed when update repo",
71 fields: fields{
72 articleRepository: mockRepository,
73 },
74 args: args{
75 ctx: ctx,
76 request: models.ArticleUpdateRequest{
77 ID: 1,
78 Title: "test update",
79 Content: "test update",
80 UpdateAt: timeNow,
81 },
82 },
83 mock: func(args args) {
84 var article = &models.Article{
85 ID: 1,
86 Title: "test",
87 Content: "test",
88 CreateAt: timeNow,
89 UpdateAt: timeNow,
90 }
91 mockRepository.On("GetByID", args.ctx, args.request.ID).Return(article, nil).Once()
92
93 article.FromUpdateRequest(args.request)
94 mockRepository.On("Update", args.ctx, article).Return(&models.Article{}, errors.New("got error"))
95 },
96 want: models.ArticleResponse{},
97 wantErr: true,
98 },
99 {
100 name: "success update",
101 fields: fields{
102 articleRepository: mockRepository,
103 },
104 args: args{
105 ctx: ctx,
106 request: models.ArticleUpdateRequest{
107 ID: 1,
108 Title: "test update",
109 Content: "test update",
110 UpdateAt: timeNow,
111 },
112 },
113 mock: func(args args) {
114 var article = &models.Article{
115 ID: 1,
116 Title: "test",
117 Content: "test",
118 CreateAt: timeNow,
119 UpdateAt: timeNow,
120 }
121 mockRepository.On("GetByID", args.ctx, args.request.ID).Return(article, nil).Once()
122
123 article.FromUpdateRequest(args.request)
124 mockRepository.On("Update", args.ctx, article).Return(article, nil).Once()
125 },
126 want: models.ArticleResponse{
127 ID: 1,
128 Title: "test update",
129 Content: "test update",
130 UpdateAt: timeNow,
131 CreateAt: timeNow,
132 },
133 wantErr: false,
134 },
135 }
136 for _, tt := range tests {
137 t.Run(tt.name, func(t *testing.T) {
138 tt.mock(tt.args)
139 u := &Usecase{
140 articleRepository: tt.fields.articleRepository,
141 }
142 got, err := u.Update(tt.args.ctx, tt.args.request)
143 if (err != nil) != tt.wantErr {
144 t.Errorf("Usecase.Update() error = %v, wantErr %v", err, tt.wantErr)
145 return
146 }
147 if !reflect.DeepEqual(got.ID, tt.want.ID) {
148 t.Errorf("Usecase.Update() = %v, want %v", got, tt.want)
149 }
150 if !reflect.DeepEqual(got.Title, tt.want.Title) {
151 t.Errorf("Usecase.Update() = %v, want %v", got, tt.want)
152 }
153 if !reflect.DeepEqual(got.Content, tt.want.Content) {
154 t.Errorf("Usecase.Update() = %v, want %v", got, tt.want)
155 }
156 if !reflect.DeepEqual(got.CreateAt, tt.want.CreateAt) {
157 t.Errorf("Usecase.Update() = %v, want %v", got, tt.want)
158 }
159 })
160 }
161}Membuat unit test fungsi Store
1func TestUsecase_Store(t *testing.T) {
2 var ctx = context.Background()
3 var timeNow = time.Now()
4 mockRepository := new(repository.MockArticleRepository)
5 type fields struct {
6 articleRepository repository.ArticleRepository
7 }
8 type args struct {
9 ctx context.Context
10 request models.ArticleCreateRequest
11 }
12 tests := []struct {
13 name string
14 fields fields
15 args args
16 want models.ArticleResponse
17 wantErr bool
18 mock func(args args)
19 }{
20 {
21 name: "failed store repository",
22 fields: fields{
23 articleRepository: mockRepository,
24 },
25 args: args{
26 ctx: ctx,
27 request: models.ArticleCreateRequest{
28 Title: "test",
29 Content: "test content",
30 CreateAt: timeNow,
31 },
32 },
33 mock: func(args args) {
34 var result int64
35 mockRepository.On("Store", args.ctx, mock.Anything).Return(result, errors.New("got error")).Once()
36 },
37 want: models.ArticleResponse{},
38 wantErr: true,
39 },
40 {
41 name: "success store repository",
42 fields: fields{
43 articleRepository: mockRepository,
44 },
45 args: args{
46 ctx: ctx,
47 request: models.ArticleCreateRequest{
48 Title: "test",
49 Content: "test content",
50 CreateAt: timeNow,
51 },
52 },
53 mock: func(args args) {
54 var result int64 = 1
55 mockRepository.On("Store", args.ctx, mock.Anything).Return(result, nil).Once()
56 },
57 want: models.ArticleResponse{
58 ID: 1,
59 Title: "test",
60 Content: "test content",
61 CreateAt: timeNow,
62 UpdateAt: timeNow,
63 },
64 wantErr: false,
65 },
66 }
67 for _, tt := range tests {
68 t.Run(tt.name, func(t *testing.T) {
69 tt.mock(tt.args)
70 u := &Usecase{
71 articleRepository: tt.fields.articleRepository,
72 }
73 got, err := u.Store(tt.args.ctx, tt.args.request)
74 if (err != nil) != tt.wantErr {
75 t.Errorf("Usecase.Store() error = %v, wantErr %v", err, tt.wantErr)
76 return
77 }
78 if !reflect.DeepEqual(got.ID, tt.want.ID) {
79 t.Errorf("Usecase.Store() = %v, want %v", got, tt.want)
80 }
81 if !reflect.DeepEqual(got.Title, tt.want.Title) {
82 t.Errorf("Usecase.Store() = %v, want %v", got, tt.want)
83 }
84 if !reflect.DeepEqual(got.Content, tt.want.Content) {
85 t.Errorf("Usecase.Store() = %v, want %v", got, tt.want)
86 }
87 })
88 }
89}Membuat unit test fungsi Delete
1func TestUsecase_Store(t *testing.T) {
2 var ctx = context.Background()
3 var timeNow = time.Now()
4 mockRepository := new(repository.MockArticleRepository)
5 type fields struct {
6 articleRepository repository.ArticleRepository
7 }
8 type args struct {
9 ctx context.Context
10 request models.ArticleCreateRequest
11 }
12 tests := []struct {
13 name string
14 fields fields
15 args args
16 want models.ArticleResponse
17 wantErr bool
18 mock func(args args)
19 }{
20 {
21 name: "failed store repository",
22 fields: fields{
23 articleRepository: mockRepository,
24 },
25 args: args{
26 ctx: ctx,
27 request: models.ArticleCreateRequest{
28 Title: "test",
29 Content: "test content",
30 CreateAt: timeNow,
31 },
32 },
33 mock: func(args args) {
34 var result int64
35 mockRepository.On("Store", args.ctx, mock.Anything).Return(result, errors.New("got error")).Once()
36 },
37 want: models.ArticleResponse{},
38 wantErr: true,
39 },
40 {
41 name: "success store repository",
42 fields: fields{
43 articleRepository: mockRepository,
44 },
45 args: args{
46 ctx: ctx,
47 request: models.ArticleCreateRequest{
48 Title: "test",
49 Content: "test content",
50 CreateAt: timeNow,
51 },
52 },
53 mock: func(args args) {
54 var result int64 = 1
55 mockRepository.On("Store", args.ctx, mock.Anything).Return(result, nil).Once()
56 },
57 want: models.ArticleResponse{
58 ID: 1,
59 Title: "test",
60 Content: "test content",
61 CreateAt: timeNow,
62 UpdateAt: timeNow,
63 },
64 wantErr: false,
65 },
66 }
67 for _, tt := range tests {
68 t.Run(tt.name, func(t *testing.T) {
69 tt.mock(tt.args)
70 u := &Usecase{
71 articleRepository: tt.fields.articleRepository,
72 }
73 got, err := u.Store(tt.args.ctx, tt.args.request)
74 if (err != nil) != tt.wantErr {
75 t.Errorf("Usecase.Store() error = %v, wantErr %v", err, tt.wantErr)
76 return
77 }
78 if !reflect.DeepEqual(got.ID, tt.want.ID) {
79 t.Errorf("Usecase.Store() = %v, want %v", got, tt.want)
80 }
81 if !reflect.DeepEqual(got.Title, tt.want.Title) {
82 t.Errorf("Usecase.Store() = %v, want %v", got, tt.want)
83 }
84 if !reflect.DeepEqual(got.Content, tt.want.Content) {
85 t.Errorf("Usecase.Store() = %v, want %v", got, tt.want)
86 }
87 })
88 }
89}
90
91func TestUsecase_Delete(t *testing.T) {
92 var ctx = context.Background()
93 var timeNow = time.Now()
94 mockRepository := new(repository.MockArticleRepository)
95 type fields struct {
96 articleRepository repository.ArticleRepository
97 }
98 type args struct {
99 ctx context.Context
100 id int64
101 }
102 tests := []struct {
103 name string
104 fields fields
105 args args
106 want bool
107 wantErr bool
108 mock func(args args)
109 }{
110 {
111 name: "failed when get by id was error",
112 fields: fields{
113 articleRepository: mockRepository,
114 },
115 args: args{
116 ctx: ctx,
117 id: 1,
118 },
119 mock: func(args args) {
120 var article *models.Article
121 mockRepository.On("GetByID", args.ctx, args.id).Return(article, errors.New("got error")).Once()
122 },
123 want: false,
124 wantErr: true,
125 },
126 {
127 name: "failed when article not found",
128 fields: fields{
129 articleRepository: mockRepository,
130 },
131 args: args{
132 ctx: ctx,
133 id: 1,
134 },
135 mock: func(args args) {
136 mockRepository.On("GetByID", args.ctx, args.id).Return(nil, nil).Once()
137
138 },
139 want: false,
140 wantErr: true,
141 },
142 {
143 name: "failed when delete repo was error",
144 fields: fields{
145 articleRepository: mockRepository,
146 },
147 args: args{
148 ctx: ctx,
149 id: 1,
150 },
151 mock: func(args args) {
152 var article = &models.Article{
153 ID: 1,
154 Title: "test",
155 Content: "test content",
156 CreateAt: timeNow,
157 UpdateAt: timeNow,
158 }
159 mockRepository.On("GetByID", args.ctx, args.id).Return(article, nil).Once()
160
161 mockRepository.On("Delete", args.ctx, args.id).Return(false, errors.New("got error")).Once()
162 },
163 want: false,
164 wantErr: true,
165 },
166 {
167 name: "success delete article",
168 fields: fields{
169 articleRepository: mockRepository,
170 },
171 args: args{
172 ctx: ctx,
173 id: 1,
174 },
175 mock: func(args args) {
176 var article = &models.Article{
177 ID: 1,
178 Title: "test",
179 Content: "test content",
180 CreateAt: timeNow,
181 UpdateAt: timeNow,
182 }
183 mockRepository.On("GetByID", args.ctx, args.id).Return(article, nil).Once()
184 mockRepository.On("Delete", args.ctx, args.id).Return(true, nil).Once()
185 },
186 want: true,
187 wantErr: false,
188 },
189 }
190 for _, tt := range tests {
191 t.Run(tt.name, func(t *testing.T) {
192 tt.mock(tt.args)
193 u := &Usecase{
194 articleRepository: tt.fields.articleRepository,
195 }
196 got, err := u.Delete(tt.args.ctx, tt.args.id)
197 if (err != nil) != tt.wantErr {
198 t.Errorf("Usecase.Delete() error = %v, wantErr %v", err, tt.wantErr)
199 return
200 }
201 if got != tt.want {
202 t.Errorf("Usecase.Delete() = %v, want %v", got, tt.want)
203 }
204 })
205 }
206}Membuat Unit test pada Handler
Pada level handler ini kita tidak membutuhkan generator mock dikarenakan kita tidak akan menggunakan handler tersebut untuk dilakukan unit test sehingga tidak perlu untuk dibuat atau di-generate.
Berbeda dengan yang lain, pada handler ini kita membuat unit test menggunakan library httptest yang mana kebutuhannya memang untuk dilakukan unit test handler yang memiliki request, response dan header dengan protokol http.
Diawali dengan membuat unit test pada inisialisasi seperti dibawah ini.
1func TestNew(t *testing.T) {
2 mockUsecase := new(usecase.MockArticleUsecase)
3 type args struct {
4 articleUsecase usecase.ArticleUsecase
5 }
6 tests := []struct {
7 name string
8 args args
9 want *Delivery
10 }{
11 {
12 name: "success",
13 args: args{
14 articleUsecase: mockUsecase,
15 },
16 want: &Delivery{
17 articleUsecase: mockUsecase,
18 },
19 },
20 }
21 for _, tt := range tests {
22 t.Run(tt.name, func(t *testing.T) {
23 New(tt.args.articleUsecase)
24 })
25 }
26}Membuat unit test pada fungsi GetAll
1func TestDelivery_GetAll(t *testing.T) {
2 mockUsecase := new(usecase.MockArticleUsecase)
3 timeNow := time.Now().UTC()
4 type fields struct {
5 articleUsecase usecase.ArticleUsecase
6 validate *validator.Validate
7 }
8 type args struct {
9 params httprouter.Params
10 }
11 tests := []struct {
12 name string
13 fields fields
14 args args
15 wantStatusCode int
16 want models.ArticleListResponse
17 mock func(args args)
18 }{
19 {
20 name: "failed get all article",
21 fields: fields{
22 articleUsecase: mockUsecase,
23 },
24 mock: func(args args) {
25 var res []models.ArticleResponse
26 mockUsecase.On("GetAll", mock.Anything).Return(res, errors.New("got error")).Once()
27 },
28 wantStatusCode: http.StatusInternalServerError,
29 want: models.ArticleListResponse{
30 HeaderResponse: models.HeaderResponse{
31 Code: http.StatusInternalServerError,
32 Status: "got error",
33 },
34 },
35 },
36 {
37 name: "success get all article",
38 fields: fields{
39 articleUsecase: mockUsecase,
40 },
41 mock: func(args args) {
42 var res = []models.ArticleResponse{
43 {
44 ID: 1,
45 Title: "test",
46 Content: "test content",
47 CreateAt: timeNow,
48 UpdateAt: timeNow,
49 },
50 }
51 mockUsecase.On("GetAll", mock.Anything).Return(res, nil).Once()
52 },
53 wantStatusCode: http.StatusOK,
54 want: models.ArticleListResponse{
55 HeaderResponse: models.HeaderResponse{
56 Code: http.StatusOK,
57 Status: "OK",
58 },
59 Data: []models.ArticleResponse{
60 {
61 ID: 1,
62 Title: "test",
63 Content: "test content",
64 CreateAt: timeNow,
65 UpdateAt: timeNow,
66 },
67 },
68 },
69 },
70 }
71 for _, tt := range tests {
72 t.Run(tt.name, func(t *testing.T) {
73 req := httptest.NewRequest("GET", "/test", nil)
74 w := httptest.NewRecorder()
75
76 tt.mock(tt.args)
77
78 d := &Delivery{
79 articleUsecase: tt.fields.articleUsecase,
80 validate: tt.fields.validate,
81 }
82 d.GetAll(w, req, tt.args.params)
83
84 resp := w.Result()
85 body, _ := io.ReadAll(resp.Body)
86
87 var result models.ArticleListResponse
88 err := json.Unmarshal(body, &result)
89 if err != nil {
90 t.Errorf("error unmarshal %v", err)
91 }
92
93 assert.Equal(t, tt.wantStatusCode, resp.StatusCode)
94 assert.Equal(t, tt.want, result)
95 })
96 }
97}Membuat unit test pada fungsi GetByID
1func TestDelivery_GetByID(t *testing.T) {
2 mockUsecase := new(usecase.MockArticleUsecase)
3 timeNow := time.Now().UTC()
4 type fields struct {
5 articleUsecase usecase.ArticleUsecase
6 validate *validator.Validate
7 }
8 type args struct {
9 params httprouter.Params
10 }
11 tests := []struct {
12 name string
13 fields fields
14 args args
15 wantStatusCode int
16 wantErr bool
17 want models.ArticleListResponse
18 mock func(args args)
19 }{
20 {
21 name: "failed get by id article",
22 fields: fields{
23 articleUsecase: mockUsecase,
24 },
25 args: args{
26 params: []httprouter.Param{{Key: "article_id", Value: "1"}},
27 },
28 mock: func(args args) {
29 var res = models.ArticleResponse{}
30 id, _ := strconv.ParseInt(args.params.ByName("article_id"), 0, 6)
31 mockUsecase.On("GetByID", mock.Anything, id).Return(res, errors.New("got error")).Once()
32 },
33 wantStatusCode: http.StatusInternalServerError,
34 want: models.ArticleListResponse{
35 HeaderResponse: models.HeaderResponse{
36 Code: http.StatusInternalServerError,
37 Status: "got error",
38 },
39 },
40 },
41 {
42 name: "failed params not found",
43 fields: fields{
44 articleUsecase: mockUsecase,
45 },
46 args: args{
47 params: []httprouter.Param{{Key: "article_id", Value: "a"}},
48 },
49 mock: func(args args) {},
50 wantStatusCode: http.StatusBadRequest,
51 want: models.ArticleListResponse{
52 HeaderResponse: models.HeaderResponse{
53 Code: http.StatusBadRequest,
54 Status: "strconv.ParseInt: parsing \"a\": invalid syntax",
55 },
56 },
57 },
58 {
59 name: "failed data not found",
60 fields: fields{
61 articleUsecase: mockUsecase,
62 },
63 args: args{
64 params: []httprouter.Param{{Key: "article_id", Value: "1"}},
65 },
66 mock: func(args args) {
67 var res = models.ArticleResponse{}
68 id, _ := strconv.ParseInt(args.params.ByName("article_id"), 0, 64)
69 mockUsecase.On("GetByID", mock.Anything, id).Return(res, nil).Once()
70 },
71 wantStatusCode: http.StatusNotFound,
72 want: models.ArticleListResponse{
73 HeaderResponse: models.HeaderResponse{
74 Code: http.StatusNotFound,
75 Status: "data not found",
76 },
77 },
78 },
79 {
80 name: "success get by id article",
81 fields: fields{
82 articleUsecase: mockUsecase,
83 },
84 args: args{
85 params: []httprouter.Param{{Key: "article_id", Value: "1"}},
86 },
87 mock: func(args args) {
88 var res = models.ArticleResponse{
89 ID: 1,
90 Title: "test",
91 Content: "test content",
92 CreateAt: timeNow,
93 UpdateAt: timeNow,
94 }
95 id, _ := strconv.ParseInt(args.params.ByName("article_id"), 0, 64)
96 mockUsecase.On("GetByID", mock.Anything, id).Return(res, nil).Once()
97 },
98 wantStatusCode: http.StatusOK,
99 want: models.ArticleListResponse{
100 HeaderResponse: models.HeaderResponse{
101 Code: http.StatusOK,
102 Status: "OK",
103 },
104 Data: []models.ArticleResponse{
105 {
106 ID: 1,
107 Title: "test",
108 Content: "test content",
109 CreateAt: timeNow,
110 UpdateAt: timeNow,
111 },
112 },
113 },
114 },
115 }
116 for _, tt := range tests {
117 t.Run(tt.name, func(t *testing.T) {
118 req := httptest.NewRequest("GET", "/test", nil)
119 w := httptest.NewRecorder()
120
121 tt.mock(tt.args)
122
123 d := &Delivery{
124 articleUsecase: tt.fields.articleUsecase,
125 validate: tt.fields.validate,
126 }
127 d.GetByID(w, req, tt.args.params)
128 resp := w.Result()
129 body, _ := io.ReadAll(resp.Body)
130
131 var result models.ArticleListResponse
132 err := json.Unmarshal(body, &result)
133 if err != nil {
134 t.Errorf("error unmarshal %v", err)
135 }
136
137 assert.Equal(t, tt.wantStatusCode, resp.StatusCode)
138 assert.Equal(t, tt.want, result)
139 })
140 }
141}Membuat unit test pada fungsi Update
1func TestDelivery_Update(t *testing.T) {
2 mockUsecase := new(usecase.MockArticleUsecase)
3 timeNow := time.Now().UTC()
4 type fields struct {
5 articleUsecase usecase.ArticleUsecase
6 validate *validator.Validate
7 }
8 type args struct {
9 params httprouter.Params
10 request models.ArticleUpdateRequest
11 }
12 tests := []struct {
13 name string
14 fields fields
15 args args
16 mock func(args args)
17 wantStatusCode int
18 wantErr bool
19 want models.ArticleListResponse
20 }{
21 {
22 name: "failed when article_id not support",
23 fields: fields{
24 articleUsecase: mockUsecase,
25 },
26 args: args{
27 params: []httprouter.Param{{Key: "article_id", Value: "a"}},
28 request: models.ArticleUpdateRequest{
29 ID: 1,
30 Title: "test data beneran",
31 Content: "test test data beneran",
32 UpdateAt: timeNow,
33 },
34 },
35 mock: func(args args) {
36
37 },
38 wantErr: true,
39 wantStatusCode: http.StatusBadRequest,
40 want: models.ArticleListResponse{
41 HeaderResponse: models.HeaderResponse{
42 Code: http.StatusBadRequest,
43 Status: "strconv.ParseInt: parsing \"a\": invalid syntax",
44 },
45 },
46 },
47 // {
48 // name: "failed when encode body",
49 // fields: fields{
50 // articleUsecase: mockUsecase,
51 // validate: validator.New(),
52 // },
53 // args: args{
54 // params: []httprouter.Param{{Key: "article_id", Value: "2"}},
55 // request: models.ArticleUpdateRequest{
56 // ID: 1,
57 // Title: "test",
58 // Content: "test",
59 // UpdateAt: timeNow,
60 // },
61 // },
62 // mock: func(args args) {},
63 // wantErr: true,
64 // wantStatusCode: http.StatusBadRequest,
65 // want: models.ArticleListResponse{
66 // HeaderResponse: models.HeaderResponse{
67 // Code: http.StatusBadRequest,
68 // Status: "strconv.ParseInt: parsing \"a\": invalid syntax",
69 // },
70 // },
71 // },
72 {
73 name: "failed when error validate",
74 fields: fields{
75 articleUsecase: mockUsecase,
76 validate: validator.New(),
77 },
78 args: args{
79 params: []httprouter.Param{{Key: "article_id", Value: "2"}},
80 request: models.ArticleUpdateRequest{
81 ID: 1,
82 Title: "test",
83 Content: "test",
84 UpdateAt: timeNow,
85 },
86 },
87 mock: func(args args) {},
88 wantErr: true,
89 wantStatusCode: http.StatusBadRequest,
90 want: models.ArticleListResponse{
91 HeaderResponse: models.HeaderResponse{
92 Code: http.StatusBadRequest,
93 Status: "Key: 'ArticleUpdateRequest.Title' Error:Field validation for 'Title' failed on the 'min' tag",
94 },
95 },
96 },
97 {
98 name: "failed when update usecase was error",
99 fields: fields{
100 articleUsecase: mockUsecase,
101 validate: validator.New(),
102 },
103 args: args{
104 params: []httprouter.Param{{Key: "article_id", Value: "2"}},
105 request: models.ArticleUpdateRequest{
106 ID: 2,
107 Title: "test data long",
108 Content: "test",
109 UpdateAt: timeNow,
110 },
111 },
112 mock: func(args args) {
113 var res models.ArticleResponse
114 mockUsecase.On("Update", mock.Anything, args.request).Return(res, errors.New("got error")).Once()
115 },
116 wantErr: true,
117 wantStatusCode: http.StatusInternalServerError,
118 want: models.ArticleListResponse{
119 HeaderResponse: models.HeaderResponse{
120 Code: http.StatusInternalServerError,
121 Status: "got error",
122 },
123 },
124 },
125 {
126 name: "success update article",
127 fields: fields{
128 articleUsecase: mockUsecase,
129 validate: validator.New(),
130 },
131 args: args{
132 params: []httprouter.Param{{Key: "article_id", Value: "2"}},
133 request: models.ArticleUpdateRequest{
134 ID: 2,
135 Title: "test data long",
136 Content: "test",
137 UpdateAt: timeNow,
138 },
139 },
140 mock: func(args args) {
141 var res models.ArticleResponse
142 mockUsecase.On("Update", mock.Anything, args.request).Return(res, nil).Once()
143 },
144 wantErr: false,
145 wantStatusCode: http.StatusOK,
146 want: models.ArticleListResponse{
147 HeaderResponse: models.HeaderResponse{
148 Code: http.StatusOK,
149 Status: "OK",
150 },
151 Data: []models.ArticleResponse{{}},
152 },
153 },
154 }
155 for _, tt := range tests {
156 t.Run(tt.name, func(t *testing.T) {
157 var b = &bytes.Buffer{}
158 if tt.args.request.Title != "" {
159 err := json.NewEncoder(b).Encode(tt.args.request)
160 if err != nil {
161 t.Fatal(err)
162 }
163 }
164
165 req := httptest.NewRequest("POST", "/test", b)
166 w := httptest.NewRecorder()
167
168 tt.mock(tt.args)
169
170 d := &Delivery{
171 articleUsecase: tt.fields.articleUsecase,
172 validate: tt.fields.validate,
173 }
174
175 d.Update(w, req, tt.args.params)
176 resp := w.Result()
177 body, _ := io.ReadAll(resp.Body)
178
179 var result models.ArticleListResponse
180 err := json.Unmarshal(body, &result)
181 if err != nil {
182 t.Errorf("error unmarshal %v", err)
183 }
184
185 assert.Equal(t, tt.wantStatusCode, resp.StatusCode)
186 assert.Equal(t, tt.want, result)
187 })
188 }
189}Membuat unit test pada fungsi Store
1func TestDelivery_Store(t *testing.T) {
2 mockUsecase := new(usecase.MockArticleUsecase)
3 timeNow := time.Now().UTC()
4 type fields struct {
5 articleUsecase usecase.ArticleUsecase
6 validate *validator.Validate
7 }
8 type args struct {
9 params httprouter.Params
10 request models.ArticleCreateRequest
11 }
12 tests := []struct {
13 name string
14 fields fields
15 args args
16 mock func(args args)
17 wantStatusCode int
18 wantErr bool
19 want models.ArticleListResponse
20 }{
21 // {
22 // name: "failed when parse json body",
23 // articleUsecase: mockUsecase,
24 // validate: validator.New(),
25 // },
26 // args: args{
27 // params: []httprouter.Param{{Key: "article_id", Value: "2"}},
28 // request: models.ArticleUpdateRequest{
29 // ID: 1,
30 // Title: "test",
31 // Content: "test",
32 // UpdateAt: timeNow,
33 // },
34 // },
35 // mock: func(args args) {},
36 // wantErr: true,
37 // wantStatusCode: http.StatusBadRequest,
38 // want: models.ArticleListResponse{
39 // HeaderResponse: models.HeaderResponse{
40 // Code: http.StatusBadRequest,
41 // Status: "strconv.ParseInt: parsing \"a\": invalid syntax",
42 // },
43 // },
44 // },
45 // },
46 {
47 name: "failed when error validate",
48 fields: fields{
49 articleUsecase: mockUsecase,
50 validate: validator.New(),
51 },
52 args: args{
53 params: []httprouter.Param{{Key: "article_id", Value: "2"}},
54 request: models.ArticleCreateRequest{
55 Title: "test",
56 Content: "test",
57 CreateAt: timeNow,
58 },
59 },
60 mock: func(args args) {},
61 wantErr: true,
62 wantStatusCode: http.StatusBadRequest,
63 want: models.ArticleListResponse{
64 HeaderResponse: models.HeaderResponse{
65 Code: http.StatusBadRequest,
66 Status: "Key: 'ArticleCreateRequest.Title' Error:Field validation for 'Title' failed on the 'min' tag",
67 },
68 },
69 },
70 {
71 name: "failed when update usecase was error",
72 fields: fields{
73 articleUsecase: mockUsecase,
74 validate: validator.New(),
75 },
76 args: args{
77 params: []httprouter.Param{{Key: "article_id", Value: "2"}},
78 request: models.ArticleCreateRequest{
79 Title: "test data long",
80 Content: "test",
81 CreateAt: timeNow,
82 },
83 },
84 mock: func(args args) {
85 var res models.ArticleResponse
86 mockUsecase.On("Store", mock.Anything, args.request).Return(res, errors.New("got error")).Once()
87 },
88 wantErr: true,
89 wantStatusCode: http.StatusInternalServerError,
90 want: models.ArticleListResponse{
91 HeaderResponse: models.HeaderResponse{
92 Code: http.StatusInternalServerError,
93 Status: "got error",
94 },
95 },
96 },
97 {
98 name: "success update article",
99 fields: fields{
100 articleUsecase: mockUsecase,
101 validate: validator.New(),
102 },
103 args: args{
104 params: []httprouter.Param{{Key: "article_id", Value: "2"}},
105 request: models.ArticleCreateRequest{
106 Title: "test data long",
107 Content: "test",
108 CreateAt: timeNow,
109 },
110 },
111 mock: func(args args) {
112 var res models.ArticleResponse
113 mockUsecase.On("Store", mock.Anything, args.request).Return(res, nil).Once()
114 },
115 wantErr: false,
116 wantStatusCode: http.StatusOK,
117 want: models.ArticleListResponse{
118 HeaderResponse: models.HeaderResponse{
119 Code: http.StatusOK,
120 Status: "OK",
121 },
122 Data: []models.ArticleResponse{{}},
123 },
124 },
125 }
126 for _, tt := range tests {
127 t.Run(tt.name, func(t *testing.T) {
128 var b = &bytes.Buffer{}
129 if tt.args.request.Title != "" {
130 err := json.NewEncoder(b).Encode(tt.args.request)
131 if err != nil {
132 t.Fatal(err)
133 }
134 }
135
136 req := httptest.NewRequest("POST", "/test", b)
137 w := httptest.NewRecorder()
138
139 tt.mock(tt.args)
140
141 d := &Delivery{
142 articleUsecase: tt.fields.articleUsecase,
143 validate: tt.fields.validate,
144 }
145
146 d.Store(w, req, tt.args.params)
147 resp := w.Result()
148 body, _ := io.ReadAll(resp.Body)
149
150 var result models.ArticleListResponse
151 err := json.Unmarshal(body, &result)
152 if err != nil {
153 t.Errorf("error unmarshal %v", err)
154 }
155 assert.Equal(t, tt.wantStatusCode, resp.StatusCode)
156 assert.Equal(t, tt.want, result)
157 })
158 }
159}Membuat unit test pada fungsi Delete
1func TestDelivery_Delete(t *testing.T) {
2 mockUsecase := new(usecase.MockArticleUsecase)
3 type fields struct {
4 articleUsecase usecase.ArticleUsecase
5 validate *validator.Validate
6 }
7 type args struct {
8 params httprouter.Params
9 request models.ArticleCreateRequest
10 }
11 tests := []struct {
12 name string
13 fields fields
14 args args
15 mock func(args args)
16 wantStatusCode int
17 wantErr bool
18 want models.ArticleListResponse
19 }{
20 {
21 name: "failed delete by id article",
22 fields: fields{
23 articleUsecase: mockUsecase,
24 },
25 args: args{
26 params: []httprouter.Param{{Key: "article_id", Value: "1"}},
27 },
28 mock: func(args args) {
29 id, _ := strconv.ParseInt(args.params.ByName("article_id"), 0, 6)
30 mockUsecase.On("Delete", mock.Anything, id).Return(false, errors.New("got error")).Once()
31 },
32 wantStatusCode: http.StatusInternalServerError,
33 want: models.ArticleListResponse{
34 HeaderResponse: models.HeaderResponse{
35 Code: http.StatusInternalServerError,
36 Status: "got error",
37 },
38 },
39 },
40 {
41 name: "failed params not found",
42 fields: fields{
43 articleUsecase: mockUsecase,
44 },
45 args: args{
46 params: []httprouter.Param{{Key: "article_id", Value: "a"}},
47 },
48 mock: func(args args) {},
49 wantStatusCode: http.StatusBadRequest,
50 want: models.ArticleListResponse{
51 HeaderResponse: models.HeaderResponse{
52 Code: http.StatusBadRequest,
53 Status: "strconv.ParseInt: parsing \"a\": invalid syntax",
54 },
55 },
56 },
57 {
58 name: "failed data not found",
59 fields: fields{
60 articleUsecase: mockUsecase,
61 },
62 args: args{
63 params: []httprouter.Param{{Key: "article_id", Value: "0"}},
64 },
65 mock: func(args args) {
66 id, _ := strconv.ParseInt(args.params.ByName("article_id"), 0, 64)
67 mockUsecase.On("Delete", mock.Anything, id).Return(false, nil).Once()
68 },
69 wantStatusCode: http.StatusNotFound,
70 want: models.ArticleListResponse{
71 HeaderResponse: models.HeaderResponse{
72 Code: http.StatusNotFound,
73 Status: "article_id was not zero",
74 },
75 },
76 },
77 {
78 name: "success delete by id article",
79 fields: fields{
80 articleUsecase: mockUsecase,
81 },
82 args: args{
83 params: []httprouter.Param{{Key: "article_id", Value: "1"}},
84 },
85 mock: func(args args) {
86 id, _ := strconv.ParseInt(args.params.ByName("article_id"), 0, 64)
87 mockUsecase.On("Delete", mock.Anything, id).Return(true, nil).Once()
88 },
89 wantStatusCode: http.StatusOK,
90 want: models.ArticleListResponse{
91 HeaderResponse: models.HeaderResponse{
92 Code: http.StatusOK,
93 Status: "OK",
94 },
95 },
96 },
97 {
98 name: "delete by article_id but data not found",
99 fields: fields{
100 articleUsecase: mockUsecase,
101 },
102 args: args{
103 params: []httprouter.Param{{Key: "article_id", Value: "1"}},
104 },
105 mock: func(args args) {
106 id, _ := strconv.ParseInt(args.params.ByName("article_id"), 0, 64)
107 mockUsecase.On("Delete", mock.Anything, id).Return(false, nil).Once()
108 },
109 wantStatusCode: http.StatusInternalServerError,
110 wantErr: true,
111 want: models.ArticleListResponse{
112 HeaderResponse: models.HeaderResponse{
113 Code: http.StatusInternalServerError,
114 Status: "unknown error",
115 },
116 },
117 },
118 }
119 for _, tt := range tests {
120 t.Run(tt.name, func(t *testing.T) {
121 var b = &bytes.Buffer{}
122 if tt.args.request.Title != "" {
123 err := json.NewEncoder(b).Encode(tt.args.request)
124 if err != nil {
125 t.Fatal(err)
126 }
127 }
128
129 req := httptest.NewRequest("GET", "/test", b)
130 w := httptest.NewRecorder()
131
132 tt.mock(tt.args)
133
134 d := &Delivery{
135 articleUsecase: tt.fields.articleUsecase,
136 validate: tt.fields.validate,
137 }
138 d.Delete(w, req, tt.args.params)
139 resp := w.Result()
140 body, _ := io.ReadAll(resp.Body)
141
142 var result models.ArticleListResponse
143 err := json.Unmarshal(body, &result)
144 if err != nil {
145 t.Errorf("error unmarshal %v", err)
146 }
147 assert.Equal(t, tt.wantStatusCode, resp.StatusCode)
148 assert.Equal(t, tt.want, result)
149 })
150 }
151}Semua telah dibuatkan unit test maka semua fungsi kita bisa test dengan baik dan sesuaikan dengan kebutuhan teman-teman. Unit test ini sangat penting sekali ketika kita ingin melakukan improvement atau menambahkan kebutuhan pada saat ada perubahan maka unit test pun digunakan untuk melakukan pengujian existing flow dan flow yang baru ditambahkan sehingga tidak terjadi kesalahan pada sistem dikemudian hari.
Selanjutnya agar mempermudah menjalankan unit test kita perlu menambahkan perintah unit test pada Makefile sebagai berikut
1mock:
2 go generate ./...
3
4test:
5 go test -race -cover ./...
perintah mock digunakan untuk melakukan generate ulang ketika kita menambahkan atau mengubah fungsi pada interface yang memiliki mock. Sedangkan untuk test kita gunakan untuk menjalankan perintah unit test dari semua fungsi pada projek ini.
Berikut ini contoh hasil kita menjalankan
1➜ make test
2go test -race -cover ./...
3? github.com/santekno/learn-golang-restful/cmd [no test files]
4? github.com/santekno/learn-golang-restful/internal/repository [no test files]
5? github.com/santekno/learn-golang-restful/internal/usecase [no test files]
6ok github.com/santekno/learn-golang-restful/internal/delivery/http (cached) coverage: 94.1% of statements
7ok github.com/santekno/learn-golang-restful/internal/middleware (cached) coverage: 100.0% of statements
8ok github.com/santekno/learn-golang-restful/internal/models (cached) coverage: 100.0% of statements
9ok github.com/santekno/learn-golang-restful/internal/repository/mysql (cached) coverage: 94.7% of statements
10? github.com/santekno/learn-golang-restful/pkg/database [no test files]
11ok github.com/santekno/learn-golang-restful/internal/usecase/article (cached) coverage: 100.0% of statements
12ok github.com/santekno/learn-golang-restful/pkg/middleware-chain (cached) coverage: 100.0% of statements
13ok github.com/santekno/learn-golang-restful/pkg/util (cached) coverage: 83.3% of statements