potholer54-fanboy
Senior Member
Ghi chú: Bài này không phải là tutorial, chỉ kể về con đường tôi đến với TDD và muốn bàn về lý do + thái độ với tests nói chung.
A) Mở màn code như bao dev khác
Từ địa ngục deploy và OT (có thể đã outdate với tình hình hiện tại)
Cách đây 7 năm khi mình còn làm việc ở VN (dù là công ty nước ngoài nhưng a e toàn người Việt và làm sản phẩm) thì mình có tình trạng như thế này, cứ mỗi lần release là toàn bộ team từ backend, frontend, mobile đều phải deploy lên server Stage và chờ QA test. QA phải test toàn bộ luồng từ đăng nhập tới tính năng, nếu có lỗi nhẹ thì dev phải fix, lỗi nặng thì delay hẳn luôn release. Hậu quả là:
Cho tới thiên đường không phải OT ngày nào
Cũng hên nhờ mình nhảy việc và công ty mới apply cái process này nên tình trạng kia hoàn toàn không xảy ra. Yêu cầu công ty mới là luôn luôn có tests kèm theo khi mà mình implement bất cứ thứ gì (vài exceptions có thể nói ở thớt khác). Trung bình 1 dòng code mình phải viết 5 dòng test.
B) Ơ, nhưng mà mình là dev tại sao mình phải viết test
Lúc đầu mình cũng bị như thế này nhưng mà CTO cũng đả thông tư tưởng cho mình như sau:
1. Mày code cái gì thì phải có tests thì mới biết đúng hay sai
2. Công ty sẽ tiết kiệm được nhiều tiền, vì mỗi lần mày test manual mà chục lần deploy như vậy thì còn mắc hơn cả automation tests
3. Khi làm việc dự án to bự thì không ai nhớ hết user cases, lỡ mày sửa code mà test failed thì đi đọc user case mà fix -> miễn sao user case cũ passed và feature mới của mày covered là okay.
4. Muốn apply continuous delivery phải có auto-tests
5. Mày không viết test thì phắng, ta thuê đứa khác
C) Ok, vậy viết test như thế nào mới hiệu quả
Thực ra test tốt là test cover tất cả các cases, edges. Nhưng trải qua quá trình Code Review thì vẫn còn sai sót do lỗi con người. Nên mình cứ cố gắng cover được tối đa càng nhiều cases càng tốt.
Nếu có 1 bug xảy ra cũng đừng thấy mình tệ hay sao cả, bình tĩnh fix bug đó. Có 2 cách fix như sau
1. Nếu nó dễ và reproducible thì: viết test để reproduce và fix bug đó. Test này sẽ giúp đảm bảo là nó sẽ không bao giờ xảy ra lại (95% bug có thể áp dụng cách này)
2. Nếu khó hoặc gấp thì xài hết mưu hèn kế bẩn để fix bug, deploy, sau đó viết test cover đoạn đó (5% thôi fen)
D) Đã trải nghiệm giờ sao luyện thành tuyệt chiêu? Cái này khuyên các bạn chưa trải qua B,C thì đừng thử
Sau khi làm quen style mới thì công ty thuê 1 ông chuyên gia TDD về dạy (cái này mình nói sơ sơ, mấy bác đọc thêm nha). Đại loại TDD là viết test trước, viết code sau. Hay còn có tên gọi khác là hãy lười nhất có thể.
Ví dụ bài toán đơn giản là giải phương trình bậc 1: ax + b = c ta có hàm như sau:
Bước 1: (hãy nhớ lười nhất có thể) Viết test cho case a = 0
Sau đó quay lại hàm chính, viết đơn giản:
Bước 2: (hãy nhớ lười nhất có thể) Viết test cho case a > 0, b = 0, c = 0
Sau đó quay lại hàm chính, viết đơn giản:
Bước 3: (hãy nhớ lười nhất có thể) Viết test cho case a > 0, b > 0, c = 0
Sau đó quay lại hàm chính, viết đơn giản:
Bước 4: (hãy nhớ lười nhất có thể) Viết test cho case a > 0, b > 0, c > 0
Sau đó quay lại hàm chính, viết đơn giản và refactor (thực tế chỉ cần xoá ở step 2 là ok)
Cứ thế lặp đi lặp lại cho tới khi bạn có đủ tất cả các cases.
Kết: khẩu quyết của TDD là
--------------------------
Mong các bác có thể chia sẻ thêm về mindset và thực tế ở công ty các bác đang làm việc hiện như thế nào. Nếu thấy thiếu sót vui lòng chỉ giúp mình.
A) Mở màn code như bao dev khác
Từ địa ngục deploy và OT (có thể đã outdate với tình hình hiện tại)
Cách đây 7 năm khi mình còn làm việc ở VN (dù là công ty nước ngoài nhưng a e toàn người Việt và làm sản phẩm) thì mình có tình trạng như thế này, cứ mỗi lần release là toàn bộ team từ backend, frontend, mobile đều phải deploy lên server Stage và chờ QA test. QA phải test toàn bộ luồng từ đăng nhập tới tính năng, nếu có lỗi nhẹ thì dev phải fix, lỗi nặng thì delay hẳn luôn release. Hậu quả là:
- Anh em OT mệt mỏi.
- Đôi lúc không thể fix và missed deadline -> bị trừ KPI.
- Nhiều khi QA sót bug -> chất lượng SP không được ok lắm.
Cho tới thiên đường không phải OT ngày nào
Cũng hên nhờ mình nhảy việc và công ty mới apply cái process này nên tình trạng kia hoàn toàn không xảy ra. Yêu cầu công ty mới là luôn luôn có tests kèm theo khi mà mình implement bất cứ thứ gì (vài exceptions có thể nói ở thớt khác). Trung bình 1 dòng code mình phải viết 5 dòng test.
B) Ơ, nhưng mà mình là dev tại sao mình phải viết test
Lúc đầu mình cũng bị như thế này nhưng mà CTO cũng đả thông tư tưởng cho mình như sau:
1. Mày code cái gì thì phải có tests thì mới biết đúng hay sai
2. Công ty sẽ tiết kiệm được nhiều tiền, vì mỗi lần mày test manual mà chục lần deploy như vậy thì còn mắc hơn cả automation tests
3. Khi làm việc dự án to bự thì không ai nhớ hết user cases, lỡ mày sửa code mà test failed thì đi đọc user case mà fix -> miễn sao user case cũ passed và feature mới của mày covered là okay.
4. Muốn apply continuous delivery phải có auto-tests
5. Mày không viết test thì phắng, ta thuê đứa khác
C) Ok, vậy viết test như thế nào mới hiệu quả
Thực ra test tốt là test cover tất cả các cases, edges. Nhưng trải qua quá trình Code Review thì vẫn còn sai sót do lỗi con người. Nên mình cứ cố gắng cover được tối đa càng nhiều cases càng tốt.
Nếu có 1 bug xảy ra cũng đừng thấy mình tệ hay sao cả, bình tĩnh fix bug đó. Có 2 cách fix như sau
1. Nếu nó dễ và reproducible thì: viết test để reproduce và fix bug đó. Test này sẽ giúp đảm bảo là nó sẽ không bao giờ xảy ra lại (95% bug có thể áp dụng cách này)
2. Nếu khó hoặc gấp thì xài hết mưu hèn kế bẩn để fix bug, deploy, sau đó viết test cover đoạn đó (5% thôi fen)
D) Đã trải nghiệm giờ sao luyện thành tuyệt chiêu? Cái này khuyên các bạn chưa trải qua B,C thì đừng thử
Sau khi làm quen style mới thì công ty thuê 1 ông chuyên gia TDD về dạy (cái này mình nói sơ sơ, mấy bác đọc thêm nha). Đại loại TDD là viết test trước, viết code sau. Hay còn có tên gọi khác là hãy lười nhất có thể.
Ví dụ bài toán đơn giản là giải phương trình bậc 1: ax + b = c ta có hàm như sau:
Code:
fun ptb1(a, b, c): number {
...
}
Bước 1: (hãy nhớ lười nhất có thể) Viết test cho case a = 0
Code:
fun test_ptb1_case_a_zero() {
ptb1(0, 1, 2) expect exception
}
Sau đó quay lại hàm chính, viết đơn giản:
Code:
fun ptb1(a, b, c): number {
if (a==0) throw exception.
}
Bước 2: (hãy nhớ lười nhất có thể) Viết test cho case a > 0, b = 0, c = 0
Code:
fun test_ptb1_case_a_not_zero_b_zero() {
ptb1(1, 0, 0) expect x = 0
}
Sau đó quay lại hàm chính, viết đơn giản:
Code:
fun ptb1(a, b, c): number {
if (a==0) throw exception.
if (b==0 && c==0) return 0
}
Bước 3: (hãy nhớ lười nhất có thể) Viết test cho case a > 0, b > 0, c = 0
Code:
fun test_ptb1_case_a_not_zero_b_not_zero_c_zero() {
ptb1(2, 1, 0) expect x = -1/2
}
Sau đó quay lại hàm chính, viết đơn giản:
Code:
fun ptb1(a, b, c): number {
if (a==0) throw exception.
if (b==0 && c==0) return 0
return (c - b) / a
}
Bước 4: (hãy nhớ lười nhất có thể) Viết test cho case a > 0, b > 0, c > 0
Code:
fun test_ptb1_case_a_not_zero_b_not_zero_c_not_zero() {
ptb1(2, 1, 3) expect x = 1
}
Sau đó quay lại hàm chính, viết đơn giản và refactor (thực tế chỉ cần xoá ở step 2 là ok)
Code:
fun ptb1(a, b, c): number {
if (a==0) throw exception.
return (c - b) / a
}
Cứ thế lặp đi lặp lại cho tới khi bạn có đủ tất cả các cases.
Kết: khẩu quyết của TDD là
- test trước, implement sau.
- lười nhất có thể nhưng k được lười nghĩ về case.
- refactor giữa các bước thoải mái, miễn là các cases đã viết passed hết.
- mỗi bước chỉ nên cover 1 case và làm càng nhanh càng tốt (lười nhất có thể, hehe)
- làm vừa đủ thứ họ yêu cầu, không nghĩ xa xôi.
--------------------------
Mong các bác có thể chia sẻ thêm về mindset và thực tế ở công ty các bác đang làm việc hiện như thế nào. Nếu thấy thiếu sót vui lòng chỉ giúp mình.
Last edited: