> For the complete documentation index, see [llms.txt](https://sons-organization-15.gitbook.io/learn-go-with-tests/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sons-organization-15.gitbook.io/learn-go-with-tests/go/integers.md).

# Integers

[**Tất cả code của chương này được lưu tại đây**](https://github.com/quii/learn-go-with-tests/tree/main/integers)

Số nguyên trong Go hoạt động đúng như bạn mong đợi. Bây giờ, hãy thử viết một hàm `Add` để kiểm tra cách chúng hoạt động.

Trước tiên, tạo file test `adder_test.go` và thêm đoạn code sau.

**Lưu ý:** Mọi file Go trong cùng một thư mục phải thuộc cùng một `package`. Hãy tổ chức code hợp lý. Nếu chưa rõ, [bài viết này](https://dave.cheney.net/2014/12/01/five-suggestions-for-setting-up-a-go-project) sẽ giúp bạn.

Khi đó, cấu trúc thư mục của bạn có thể trông như thế này:

```
learnGoWithTests
    |
    |-> helloworld
    |    |- hello.go
    |    |- hello_test.go
    |
    |-> integers
    |    |- adder_test.go
    |
    |- go.mod
    |- README.md
```

## Viết test trước tiên

```go
package integers

import "testing"

func TestAdder(t *testing.T) {
	sum := Add(2, 2)
	expected := 4

	if sum != expected {
		t.Errorf("expected '%d' but got '%d'", expected, sum)
	}
}
```

Ở đây chúng ta dùng `%d` thay vì `%q` trong chuỗi định dạng — `%d` dùng cho số nguyên, còn `%q` dùng cho chuỗi.

Lần này chúng ta không dùng package `main` nữa, mà định nghĩa package `integers`. Đúng như tên gọi, package này chứa các hàm làm việc với số nguyên, như `Add`.

## Chạy thử test

Chạy lệnh `go test`

Quan sát lỗi biên dịch

`./adder_test.go:6:9: undefined: Add`

Lỗi này nghĩa là hàm `Add` chưa được định nghĩa

## Viết lượng code tối thiểu để chạy test và kiểm tra kết quả lỗi

Chỉ viết đủ code để trình biên dịch không báo lỗi – *không hơn không kém*. Điều này giúp đảm bảo rằng bài test sẽ thất bại với lý do chính xác mà chúng ta mong đợi.

```go
package integers

func Add(x, y int) int {
	return 0
}
```

Nhớ rằng, khi có nhiều tham số cùng kiểu dữ liệu (trong trường hợp này là hai số nguyên), thay vì viết `(x int, y int)`, bạn có thể rút gọn thành `(x, y int)`.

Bây giờ hãy chạy lại test, chúng ta sẽ thấy kết quả báo lỗi chính xác:

`adder_test.go:10: expected '4' but got '0'`

Trong [phần trước](https://sons-organization-15.gitbook.io/learn-go-with-tests/go/pages/HrdU4TkEtP9YVTxaZLHA#one...last...refactor?), chúng ta đã học về *named return value*, nhưng ở đây không sử dụng. Named return value nên dùng khi ý nghĩa giá trị trả về không rõ ràng. Ở đây, hàm `Add` đã rõ ràng là cộng hai số, nên không cần.

Bạn có thể tham khảo thêm về vấn đề này trong [wiki chính thức của Go](https://go.dev/wiki/CodeReviewComments#named-result-parameters).

## Viết đủ code để test chạy thành công

Theo đúng quy trình TDD, bây giờ chúng ta chỉ nên viết lượng code *tối thiểu cần thiết* để test *chạy thành công*. Một lập trình viên thực dụng có thể viết như sau:

```go
func Add(x, y int) int {
	return 4
}
```

Bạn có thể nghĩ TDD thật vô nghĩa khi chỉ viết đủ để test pass mà không giải quyết vấn đề thực sự.

Chúng ta có thể viết thêm test với số khác để buộc nó fail, nhưng điều đó giống [trò mèo vờn chuột](https://en.m.wikipedia.org/wiki/Cat_and_mouse).

Sau này, khi quen với Go, chúng ta sẽ tìm hiểu *Property-Based Testing* — kỹ thuật giúp phát hiện lỗi mà không cần chơi trò đuổi bắt với test.

Còn bây giờ, hãy sửa lỗi đúng cách!

```go
func Add(x, y int) int {
	return x + y
}
```

Nếu bạn chạy lại test, nó nên thành công.

## Refactor

Không có nhiều điều cần cải thiện trong đoạn code ở đây.

Trước đó, chúng ta đã thấy rằng khi đặt tên cho giá trị trả về, nó sẽ xuất hiện trong tài liệu và hầu hết các trình soạn thảo của lập trình viên.

Điều này rất hữu ích vì nó giúp code dễ sử dụng hơn. Tốt nhất là người dùng có thể hiểu cách sử dụng hàm chỉ bằng cách nhìn vào kiểu dữ liệu trả về và tài liệu đi kèm.

Bạn có thể thêm tài liệu cho hàm bằng cách viết chú thích phía trên nó. Những chú thích này sẽ xuất hiện trong Go Doc, giống như khi bạn xem tài liệu của thư viện tiêu chuẩn.

```go
// Add takes two integers and returns the sum of them.
func Add(x, y int) int {
	return x + y
}
```

### Các Test Ví Dụ (Testable Examples)

Nếu muốn làm tốt hơn nữa, bạn có thể tạo các [Test Ví Dụ](https://blog.golang.org/examples). Bạn sẽ thấy nhiều ví dụ như vậy trong tài liệu của thư viện chuẩn.

Thường thì các code ví dụ nằm bên ngoài code thực tế, chẳng hạn trong file README, dễ bị lỗi thời và không còn chính xác vì chúng không được kiểm tra thường xuyên.

Các hàm ví dụ trong Go được biên dịch mỗi khi chạy test. Vì vậy, bạn có thể chắc chắn rằng ví dụ trong tài liệu luôn phản ánh đúng hành vi hiện tại của code

Các hàm ví dụ phải bắt đầu bằng từ khóa `Example` (giống như hàm test bắt đầu bằng `Test`) và được đặt trong các file `_test.go` của package.

Hãy thêm hàm `ExampleAdd` vào file `adder_test.go`.

```go
func ExampleAdd() {
	sum := Add(1, 5)
	fmt.Println(sum)
	// Output: 6
}
```

(Nếu trình soạn thảo của bạn không tự động nhập các gói cần thiết, bước biên dịch sẽ thất bại vì thiếu `import "fmt"` trong `adder_test.go`. Bạn nên tìm hiểu cách cấu hình trình soạn thảo để tự động xử lý các lỗi kiểu này.)

Thêm đoạn code này sẽ giúp ví dụ xuất hiện trong tài liệu, làm cho code của bạn dễ tiếp cận hơn. Nếu sau này code thay đổi khiến ví dụ không còn hợp lệ, quá trình biên dịch sẽ thất bại, giúp bạn phát hiện lỗi sớm.

Khi chạy test suite của package, bạn sẽ thấy hàm `ExampleAdd` được thực thi mà không cần thiết lập gì thêm.

```bash
$ go test -v
=== RUN   TestAdder
--- PASS: TestAdder (0.00s)
=== RUN   ExampleAdd
--- PASS: ExampleAdd (0.00s)
```

Lưu ý định dạng đặc biệt của chú thích `// Output: 6`. Khi có chú thích này, ví dụ không chỉ được biên dịch mà còn được thực thi trong quá trình test. Hãy thử xóa dòng `// Output: 6`, sau đó chạy `go test`, bạn sẽ thấy `ExampleAdd` không còn được chạy nữa.

Các ví dụ không có chú thích đầu ra vẫn hữu ích để minh họa cách sử dụng code, đặc biệt đối với các trường hợp không thể unit test, chẳng hạn như truy cập mạng. Tuy nhiên, chúng vẫn đảm bảo code có thể biên dịch được.

Để xem tài liệu ví dụ, bạn có thể sử dụng {pkgsite}. Điều hướng đến thư mục dự án, sau đó chạy `pkgsite -open .`. Trình duyệt sẽ mở trang `http://localhost:8080`, nơi bạn có thể xem danh sách tất cả các gói trong thư viện chuẩn của Go cũng như các gói bên thứ ba mà bạn đã cài đặt.

Tại đây, bạn sẽ tìm thấy tài liệu cho gói của mình. Điều hướng đến `github.com/quii/learn-go-with-tests`, vào phần `Integers`, chọn `func Add`, rồi mở rộng mục `Example`. Bạn sẽ thấy ví dụ mà bạn đã thêm cho `sum := Add(1, 5)`.

Nếu bạn công khai code cùng các ví dụ, tài liệu của bạn cũng sẽ xuất hiện trên [pkg.go.dev](https://pkg.go.dev/). Ví dụ, [đây](https://pkg.go.dev/github.com/quii/learn-go-with-tests/integers/v2) là API cuối cùng của chương này. Trang web này giúp bạn tìm kiếm tài liệu của các package trong thư viện chuẩn và các package bên thứ ba dễ dàng hơn.

## Tổng kết

Những gì chúng ta đã học được:

* Thực hành thêm về quy trình TDD
* Làm việc với số nguyên và phép cộng
* Viết tài liệu rõ ràng hơn để người dùng có thể nhanh chóng hiểu cách sử dụng code
* Cách viết ví dụ minh họa cho code, đồng thời đảm bảo chúng luôn được kiểm tra cùng với test suite
