Hướng Dẫn Unit Testing Trong Golang Với Thư Viện Stretchr/testify và Mockery
Trong bài viết này, chúng ta sẽ cùng khám phá một ví dụ cụ thể về cách sử dụng thư viện stretchr/testify
và mockery
để thực hiện mocking trong Golang. Bài viết sẽ bao gồm các chủ đề như testing với assertions, sử dụng gói require
cho các assertions nghiêm ngặt, kiểm thử HTTP handlers, và mocking các dependencies với mockery
.
Kịch Bản Kiểm Thử
Giả sử chúng ta có một dịch vụ cần lấy thông tin người dùng từ một API bên ngoài. Chúng ta sẽ thực hiện kiểm thử cho:
- Chức năng của dịch vụ.
- Tích hợp của dịch vụ với một external client.
- Mocking client bên ngoài.
Cấu Trúc Dự Án
/project
│
├── main.go
├── service.go
├── service_test.go
├── user_client.go
├── mocks/
│ └── UserClient.go (generated by mockery)
└── go.mod
Tổng Quan Về Source Code
1. user_client.go
File này định nghĩa một interface để tương tác với một external user API.
go
package project
type User struct {
ID int
Name string
}
type UserClient interface {
GetUserByID(id int) (*User, error)
}
2. service.go
File này chứa một service sử dụng UserClient để lấy thông tin người dùng.
go
package project
import "fmt"
type UserService struct {
client UserClient
}
func NewUserService(client UserClient) *UserService {
return &UserService{client: client}
}
func (s *UserService) GetUserDetails(id int) (string, error) {
user, err := s.client.GetUserByID(id)
if err != nil {
return "", fmt.Errorf("failed to get user: %w", err)
}
return fmt.Sprintf("User: %s (ID: %d)", user.Name, user.ID), nil
}
3. Tạo Mock với Mockery
Để tạo mock cho UserClient
, bạn cần sử dụng mockery
với lệnh:
bash
mockery --name=UserClient --output=./mocks
Lệnh này sẽ tạo ra một file mock trong thư mục mocks/UserClient.go
.
4. service_test.go
Bây giờ, chúng ta sẽ viết test cho UserService
sử dụng stretchr/testify
để thực hiện assertions và các file mock đã được tạo trước đó bằng mockery
.
go
package project_test
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"project"
"project/mocks"
)
func TestUserService_GetUserDetails_Success(t *testing.T) {
// Tạo một mock client mới
mockClient := new(mocks.UserClient)
// Định nghĩa kết quả mà mock sẽ trả về khi gọi `GetUserByID`
mockClient.On("GetUserByID", 1).Return(&project.User{
ID: 1,
Name: "John Doe",
}, nil)
// Tạo UserService với mock client
service := project.NewUserService(mockClient)
// Test phương thức GetUserDetails
result, err := service.GetUserDetails(1)
// Sử dụng `require` cho các kiểm tra lỗi
require.NoError(t, err)
require.NotEmpty(t, result)
// Sử dụng `assert` để kiểm tra giá trị kết quả
assert.Equal(t, "User: John Doe (ID: 1)", result)
// Đảm bảo rằng phương thức `GetUserByID` đã được gọi chính xác một lần
mockClient.AssertExpectations(t)
}
func TestUserService_GetUserDetails_Error(t *testing.T) {
// Tạo một mock client mới
mockClient := new(mocks.UserClient)
// Định nghĩa kết quả mà mock sẽ trả về khi gọi `GetUserByID` và có lỗi xảy ra
mockClient.On("GetUserByID", 2).Return(nil, errors.New("user not found"))
// Tạo UserService với mock client
service := project.NewUserService(mockClient)
// Test phương thức GetUserDetails
result, err := service.GetUserDetails(2)
// Sử dụng `require` cho các kiểm tra lỗi
require.Error(t, err)
assert.Contains(t, err.Error(), "user not found")
// Đảm bảo rằng kết quả là rỗng
assert.Empty(t, result)
// Đảm bảo rằng phương thức `GetUserByID` đã được gọi chính xác một lần
mockClient.AssertExpectations(t)
}
Những Điểm Chính Của Ví Dụ Trên
-
Assertions Với Testify:
- Các gói
assert
vàrequire
được sử dụng để thực hiện kiểm tra khác nhau. require
được sử dụng cho các test case mà nếu thất bại thì bài test sẽ dừng lại ngay lập tức.assert
được sử dụng cho các test case mà có thể tiếp tục ngay cả khi thất bại.
- Các gói
-
Mocking Với Mockery:
mockery
tạo ra một mock cho interfaceUserClient
.- Trong bài test, mock được cấu hình bằng
.On()
để chỉ định đầu vào mong muốn và.Return()
để chỉ định đầu ra tương ứng. AssertExpectations
xác minh rằng phương thức mock đã được gọi với các đầu vào mong muốn.
-
Xử Lý Lỗi Trong Kiểm Thử:
- Một bài test kiểm tra trường hợp thành công, trong khi một bài khác kiểm tra cách dịch vụ xử lý lỗi từ
UserClient
.
- Một bài test kiểm tra trường hợp thành công, trong khi một bài khác kiểm tra cách dịch vụ xử lý lỗi từ
Bài viết này đã cung cấp một cái nhìn tổng quan về các chức năng cơ bản của stretchr/testify
cho assertions và mocking với mockery
. Đây là một cách tiếp cận có cấu trúc và dễ bảo trì cho việc viết unit tests trong Golang. Cảm ơn bạn đã theo dõi!
source: viblo