thảo luận Chia sẻ về Unit Testing và TDD | theNEXTvoz…
thảo luận - Chia sẻ về Unit Testing và TDD | theNEXTvoz
potholer54-fanboy
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à:
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:
beocaca
hay quá thím, có động lực viết test vãi
thím chia sẻ thêm integration test, BDD luôn đi
Nepgear.
Bài này cũng tương tự mấy cái bài "how to write unit tests" khi google, mang tính chất đọc cho vui
Newbie mà đọc thì càng ko biết phải làm gì.
Muốn chia sẻ Unit Testings hay TDD tốt nhất phải có 1 cái real-world application, mà cái này thì phải join cty có viết tests mới học dc
Bài này cũng tương tự mấy cái bài "how to write unit tests" khi google, mang tính chất đọc cho vui
Newbie mà đọc thì càng ko biết phải làm gì.
Muốn chia sẻ Unit Testings hay TDD tốt nhất phải có 1 cái real-world application, mà cái này thì phải join cty có viết tests mới học dc
Đúng là phải có real-world app mới học nhanh được.
Bài này mình viết chủ yếu muốn bàn về lý do và thái độ với tests, còn tutorial thì google nhiều mà.
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.
chắc mình làm toàn cty cùi, k viết test. Đang định viết cái sample, có unit test đầy đủ đi pv có ổn k bác
Bài này cũng tương tự mấy cái bài "how to write unit tests" khi google, mang tính chất đọc cho vui
Newbie mà đọc thì càng ko biết phải làm gì.
Muốn chia sẻ Unit Testings hay TDD tốt nhất phải có 1 cái real-world application, mà cái này thì phải join cty có viết tests mới học dc
Đang định cmt y chang vầy thì thấy này nên ưng luôn
Mấy cái test này nó như lúc mới đi tìm việc vậy (muốn có việc phải kn, muốn có kn phải làm việc): phải làm thực tế mới hiểu nhưng để được làm thực tế thì phải có kinh nghiệm về nó (hoặc được ai đó chỉ dẫn), ko thì loay hoay rất mất tg
chắc mình làm toàn cty cùi, k viết test. Đang định viết cái sample, có unit test đầy đủ đi pv có ổn k bác
Sent from Samsung SM-G960F using vozFApp
Như 2 comments của 2 bạn kia nói cũng đúng, nếu không có môi trường + người chỉ thì khó làm cái này lắm. Mình gợi ý cách học như sau ha
Về công việc trên cty: tập viết thêm test vào, dù nó không required, thằng nào cười kệ nó, chỉ cần hiểu vì sao mình phải viết test là ok.
Tạo 1 pet project trên github, và thử build 1 CI có code quality, code coverage report (ví dụ: coveralls.io, travis-ci, codeclimate). Rồi cứ tập code cho tới khi coverage được 100%, quality được A.
Rất mất thời gian. Tốn thời gian hơn phần code chính nữa.
Ruaconlonton123
Chấm hóng các bác chia sẻ về việc viết TDD ạ, mấy dự án em tham gia chưa có cái nào thấy dùng
monster_hunter
Em thấy cái việc khó nhất là ngồi nghĩ ra cases để test. Mún nghĩ ra dc như vại thì chắc chắn phải có kinh nghiệm trải qua các case đó thì mới nghĩ ra dc.
Sent from Vsmart Active 3 using vozFApp
kumokumo
Vấn đề lớn nhất của tdd là chi phí. Bạn mới nhìn ở góc độ kỹ thuật chứ chưa ở vị trí quản lý dự án. Tdd nói qua thì mọi thứ đều tuyệt vời, trừ chi phí
TDD làm bài bản, mình thấy tính riêng LOC phải gấp 4-5 lần phần code dc test.
Đúng nha, mình có nói trong bài viết 1 line code 5 lines test
Về chi phí thì tuỳ dự án, mình chỉ đứng dưới góc độ kỹ thuật. Làm TDD chi phí cao là bình thường, bù lại chất lượng dự án sẽ tốt hơn rất nhiều, phù hợp cho team sản phẩm hơn là outsource.
TDD chạy theo feature, khi pass đủ case là xong, trong khi mục tiêu của software là hướng tới tính abstraction. Làm TDD mà ko dành thêm thời gian để chỉnh sửa design thì không ổn. Mình nghĩ TDD phù hợp với dự án nào mà apply CICD, đỡ risk.
TDD chạy theo feature, khi pass đủ case là xong, trong khi mục tiêu của software là hướng tới tính abstraction. Làm TDD mà ko dành thêm thời gian để chỉnh sửa design thì không ổn. Mình nghĩ TDD phù hợp với dự án nào mà apply CICD, đỡ risk.
Sent using vozFApp
Mục tiêu của software cuối cùng là khách hàng (hay cuối cuối cùng là money), khách không quan tâm abstraction và sếp sale không quan tâm tới code đẹp xấu.
Về design thì có bước Code Review lo rồi, ví dụ xài pattern sai thì khi sửa lại cũng không phải sửa quá nhiều tests nếu có 1 integration test cover feature.
Mục tiêu của software cuối cùng là khách hàng (hay cuối cuối cùng là money), khách không quan tâm abstraction và sếp sale không quan tâm tới code đẹp xấu.
Về design thì có bước Code Review lo rồi, ví dụ xài pattern sai thì khi sửa lại cũng không phải sửa quá nhiều tests nếu có 1 integration test cover feature.
Tớ đang là dev nên tớ đứng ở góc nhìn của dev thôi. Product càng hướng tới abstraction thì càng tốt. TDD thì vẫn đang thành công ở nhiều dự án. Tương lai thì chưa biết sao.
Sent using vozFApp
leo195
Và thường cty nào áp dụng TDD thì team QA vứt xó, có còn mịe gì để test đâu.
Tuy nhiên trình dev cùi thì áp dụng TDD ko ổn. Không có mindset nghĩ ra nhiều case thì mất thời gian, làm ko hiệu quả nên chung quy vẫn áp dụng cách truyền thống là dev viết code QA tìm bug.
servuskevin
Tùy sản phẩm mà TDD nó mang lại giá trị nhiều ít. Ví dụ làm tầng Core thì TDD là phải có. Vì chủ yếu là về logic.
Nhưng ở tầng Application nó thì đôi khi nó không cần. Ví dụ như TDD cho Repository là ko cần.
Rồi tới tầng UI thì TDD là ác mộng nếu làm cho Web. Bên FE mà refactor CSS class là sửa lại Unit Test chết choa luôn.
Nói vậy thôi, chứ một sản phẩm có Unit Test là rất tốt. Nhưng đa phần toàn làm để khoe Test Coverage thôi chứ thật sự nhìn vào code chả có ý nghĩa gì.
karina hart
TDD cho dữ tới lúc bị dí cũng kemeno mà ignore hết
Và thường cty nào áp dụng TDD thì team QA vứt xó, có còn mịe gì để test đâu.
Tuy nhiên trình dev cùi thì áp dụng TDD ko ổn. Không có mindset nghĩ ra nhiều case thì mất thời gian, làm ko hiệu quả nên chung quy vẫn áp dụng cách truyền thống là dev viết code QA tìm bug.
Lý thuyết này đâu ra thế, QA vẫn cần đứng ra để test từ manual cho tới automation để đạt high trust, QA là chốt chặn cuối trc khi lên production.
TDD/Unit testing là từ dev viết và nó đem lại confident cho dev, chứ QA ko liên quan gì ở đây.
Tất nhiên là TDD tùy projects mà apply, vd như banking, fintech,... có apply TDD lại chả tăng trust nhiều hơn là ko viết.
NecNec
Vẫn ko hiểu làm sao viết Unit test cho React hay Angular, giờ làm nguyên 1 page trong một component thì viết test kiểu gì ?
Ai pro vô thông não giúp mình cái
servuskevin
@NecNec Angular nó có hỗ trợ viết unit test cho UI component mà.
Lý thuyết này đâu ra thế, QA vẫn cần đứng ra để test từ manual cho tới automation để đạt high trust, QA là chốt chặn cuối trc khi lên production.
TDD/Unit testing là từ dev viết và nó đem lại confident cho dev, chứ QA ko liên quan gì ở đây.
Tất nhiên là TDD tùy projects mà apply, vd như banking, fintech,... có apply TDD lại chả tăng trust nhiều hơn là ko viết.
Cái này tuỳ team thôi, chỗ tôi ngay từ đầu không có team QA vì quan điểm là dev phải chịu trách nhiệm với chất lượng code của mình. Thêm thông tin là số bug nhảm cực kì ít, có bug chỉ là edge cases. Sau này khi phát triển mạnh lên mới setup team QA và nhiệm vụ chính của họ là end2end tests chớ không phải là manual/acceptance test.
ko lo, nhưng cứ bị dí deadline là nghỉ việc thì chắc 1 năm e làm chục cty mất
Cái này tuỳ sếp có tâm hay không và văn hoá team nữa. Chỗ tôi chỉ cần giải thích rõ ràng lý do vì sao missed deadline, goal, sẽ làm gì để improve là sếp ok (sếp biết sếp ép là a e nghỉ
)
Edit: Moé mới sáng nên chưa tỉnh ngủ. Chuyện dí thì mình cứ capacity bao nhiêu thì nhận bấy nhiêu thôi, có number rõ ràng chả ai ép được
Và thường cty nào áp dụng TDD thì team QA vứt xó, có còn mịe gì để test đâu.
Tuy nhiên trình dev cùi thì áp dụng TDD ko ổn. Không có mindset nghĩ ra nhiều case thì mất thời gian, làm ko hiệu quả nên chung quy vẫn áp dụng cách truyền thống là dev viết code QA tìm bug.
Xàm vừa thôi, TDD ko phải thần thánh mà ko capture hết bug, logic code lúc TDD thì có thể phát hiện ra sớm hơn khi test trực tiếp trên UI, hay E2E nhưng TDD cũng méo thể thay thế dc việc testing để đảm bảo quality
Vẫn ko hiểu làm sao viết Unit test cho React hay Angular, giờ làm nguyên 1 page trong một component thì viết test kiểu gì ?
Ai pro vô thông não giúp mình cái
testing front-end mình thấy có hai loại ( theo những gì mình thấy, hồi trước làm fullstack hơn một năm xong nhảy qua đội data-infra )
logic testing: các thuật toán hay hàm dùng để xử lý dữ liệu từ dạng này sang dạng khác, hoặc làm các công vụ không liên quan đến UI, có thể dùng jest/mocha
testing front-end mình thấy có hai loại ( theo những gì mình thấy, hồi trước làm fullstack hơn một năm xong nhảy qua đội data-infra )
logic testing: các thuật toán hay hàm dùng để xử lý dữ liệu từ dạng này sang dạng khác, hoặc làm các công vụ không liên quan đến UI, có thể dùng jest/mocha
Hồi xưa mình viết react cũng test tương tự vậy, mấy cái như selector, reducer thì là pure logic nên dễ test, component thì như bác nói. Nhưng giờ kiến thức đó outdate rồi, nhà nhà giờ xài hook với query mình chưa làm nên k dám ý kiến.
Cái này với các bạn mới học thì rất ngại vì thường những ví dụ là những cái function nó trivial, tầm thường quá. Nhưngđến khi trong code thì chả có cái nào kiểuđó cả nên không biết bắtđầu từđâu cả.Để làmđược cáinày thì phải code xong cứng tay rồi học thì sẽ dễ hìnhdung hơn.
XiangLiang
Bác nào cho xin mã nguồn của integrate và unit test vó partern full với. Chứ gõ tìm thì toàn ra mấy cái test a+ b = c??? What??
Project thực tế phải test đc CRUD, đc bussiness với các service liên kêtz vs nhau, chứ tại sao lại unit test assert xem số a + số b có bằng C không đọc phát chán mà k áp dụng đc j cả.
Bác nào cho xin mã nguồn của integrate và unit test vó partern full với. Chứ gõ tìm thì toàn ra mấy cái test a+ b = c??? What??
Project thực tế phải test đc CRUD, đc bussiness với các service liên kêtz vs nhau, chứ tại sao lại unit test assert xem số a + số b có bằng C không đọc phát chán mà k áp dụng đc j cả.
Mấy open source đa số có test mà. Nếu không thấy bác cứ mạnh dạn thử 1 feature full tests, từ integration tới unit. Xài tool biết nó cover bao nhiêu %. Cố cover hết các case, branch là hình dung được thôi.
Mình cũng đang làm một project trên cloud. Cũng lần đầu apply TDD, phải nghỉ ra các test cases sau khi design rồi mới code.
Tính ra thời gian code ngắn lại, ít bugs tào lao hơn.
Có điều mình test với db, một test case ứng với một lần tạo db rồi xóa nên lâu kinh.
Ai có kinh nghiệm cho mình hỏi cách nào reuse db cho nhiều test cases được không.
Mình cũng đang làm một project trên cloud. Cũng lần đầu apply TDD, phải nghỉ ra các test cases sau khi design rồi mới code.
Tính ra thời gian code ngắn lại, ít bugs tào lao hơn.
Có điều mình test với db, một test case ứng với một lần tạo db rồi xóa nên lâu kinh.
Ai có kinh nghiệm cho mình hỏi cách nào reuse db cho nhiều test cases được không.
Mình cũng đang làm một project trên cloud. Cũng lần đầu apply TDD, phải nghỉ ra các test cases sau khi design rồi mới code.
Tính ra thời gian code ngắn lại, ít bugs tào lao hơn.
Có điều mình test với db, một test case ứng với một lần tạo db rồi xóa nên lâu kinh.
Ai có kinh nghiệm cho mình hỏi cách nào reuse db cho nhiều test cases được không.
cái test với DB thì gọi là integration test,
cái test với chỉ logic không mới gọi là unit test.
cái test có sử dụng các nghiệp vụ thì nó gọi là BDD.
Nên là đang không rõ mọi người nói về TDD bên trên là cụ thể nói về loại nào.
Mình cũng đang làm một project trên cloud. Cũng lần đầu apply TDD, phải nghỉ ra các test cases sau khi design rồi mới code.
Tính ra thời gian code ngắn lại, ít bugs tào lao hơn.
Có điều mình test với db, một test case ứng với một lần tạo db rồi xóa nên lâu kinh.
Ai có kinh nghiệm cho mình hỏi cách nào reuse db cho nhiều test cases được không.
Nếu bác xài RDBM thì cho cái test vào transaction ở setUp nhưng đừng commit transaction đó. Còn xài NoSQL thì seed db rồi delete cả db cho khoẻ. Cái Seed cũng quan trọng, bác đầu tư phần này đáng lắm.
Edit: kinh nghiệm là không nên re-use db cho nhiều test cases. Tôi đoán bác đang xài No-SQL nên gặp vấn đề này, 1 db cùng lắm nên xài cho 1 test suite, đừng nên xài chung quá nhiều. Có 2 lý do:
DB là lưu state, trong khi test nên là stateless, nếu lưu db mà không xoá sẽ có nhiều side-effect và maybe là flaky tests.
Tách db để run test multi-thread -> deploy nhanh hơn.
Nếu bác xài RDBM thì cho cái test vào transaction ở setUp nhưng đừng commit transaction đó. Còn xài NoSQL thì seed db rồi delete cả db cho khoẻ. Cái Seed cũng quan trọng, bác đầu tư phần này đáng lắm.
Edit: kinh nghiệm là không nên re-use db cho nhiều test cases. Tôi đoán bác đang xài No-SQL nên gặp vấn đề này, 1 db cùng lắm nên xài cho 1 test suite, đừng nên xài chung quá nhiều. Có 2 lý do:
DB là lưu state, trong khi test nên là stateless, nếu lưu db mà không xoá sẽ có nhiều side-effect và maybe là flaky tests.
Tách db để run test multi-thread -> deploy nhanh hơn.
Tôi nghĩ #39 nói rất đúng. Thật sự tôi không rõ định nghĩa Unit Test của mỗi người là như thế nào để chúng ta có thể thảo luận được một cách tử tế về chủ đề này
(1) Nếu Unit Test nhắm tới các khối code nhỏ nhất như #1 nói đến hoặc như nhiều người "preach" thì đúng... nó sẽ rất "dễ" ... dễ đến mức Unit Testing không còn ý nghĩa gì nữa ?
Để có được mức độ Isolation như vậy thì Unit Test này phải test các khối code rất nhỏ, các phép toán như createSaltedPassword() hay CustomGraphGetNodeByPropertyValue()
(2) Tôi tin phần lớn các test chúng ta viết rơi vào trường hơp "
Integration Test / Integration Unit Test" khi đã có đủ các thể loại ban bệ (dependency) trong source code của bạn. Giả sử bạn có 1 hàm như thế này - từ Jellyfin
Nhìn thì có vẻ rất đơn giản - chỉ có 2 parameter. Tuy nhiên mỗi object được gửi vào có một
tấn property và bản thân hàm này cũng cho ra 1
tấn các kết quả khác nhau. Vậy chúng ta làm gì với nó?
Mock tất cả mọi thứ và test tất cả mọi thứ? ... well nhắc đến mock thì nó lại là một ổ sâu khác
Không test nó?
"Hàm trên chưa được viết tốt, khi chúng ta đã phân chia công việc một cách tử tế / đủ nhỏ để unit test thì sẽ không bao giờ xảy ra trường hợp như thế này, bản thân các hàm khác nhỏ hơn được hàm này gọi đã được test thì chúng ta sẽ không bao giờ gặp trường hợp như thế này" - Tôi nghĩ lập luận này là cứt bò. Nếu đập tất cả mọi thứ nhỏ đến mức có thể làm một cái "unit test" hoàn toàn độc lập, nhỏ đến mức trivial như ở điểm (1) thì cái chúng ta có sẽ là x10 số lượng hàm ( Để giảm độ phức tạp từng hàm?) , x10 số lượng class (Để giảm số property trên mỗi class?) , x10 độ sâu call stack ( Hậu quả của việc xẻ nhỏ hàm) và x100 LOC mà không mang lại giá trị gì thật sự cả
Nhận định cá nhân: Nếu một developer thực sự nghĩ trước khi implement một thứ gì trong
cùng một lượng thời gian với một developer dùng thời gian đó để nghĩ và viết unit test thì tôi tin tưởng nhiều hơn vào chất lượng của người
nghĩ và không viết unit test. Thứ mà unit test mang lại là nó
ép developer phải nghĩ. Chất lượng code tốt hơn vì developer đó đã nghĩ kỹ trước khi implement, không phải vì bản thân việc viết Unit Test
Điểm tốt khác là sớm phát hiện breaking changes. Cái này thực sự là một điểm + lớn, không phải bàn cãi
Tôi nghĩ #39 nói rất đúng. Thật sự tôi không rõ định nghĩa Unit Test của mỗi người là như thế nào để chúng ta có thể thảo luận được một cách tử tế về chủ đề này
(1) Nếu Unit Test nhắm tới các khối code nhỏ nhất như #1 nói đến hoặc như nhiều người "preach" thì đúng... nó sẽ rất "dễ" ... dễ đến mức Unit Testing không còn ý nghĩa gì nữa ?
Để có được mức độ Isolation như vậy thì Unit Test này phải test các khối code rất nhỏ, các phép toán như createSaltedPassword() hay CustomGraphGetNodeByPropertyValue()
(2) Tôi tin phần lớn các test chúng ta viết rơi vào trường hơp "
Integration Test / Integration Unit Test" khi đã có đủ các thể loại ban bệ (dependency) trong source code của bạn. Giả sử bạn có 1 hàm như thế này - từ Jellyfin
Nhìn thì có vẻ rất đơn giản - chỉ có 2 parameter. Tuy nhiên mỗi object được gửi vào có một
tấn property và bản thân hàm này cũng cho ra 1
tấn các kết quả khác nhau. Vậy chúng ta làm gì với nó?
Mock tất cả mọi thứ và test tất cả mọi thứ? ... well nhắc đến mock thì nó lại là một ổ sâu khác
Không test nó?
"Hàm trên chưa được viết tốt, khi chúng ta đã phân chia công việc một cách tử tế / đủ nhỏ để unit test thì sẽ không bao giờ xảy ra trường hợp như thế này, bản thân các hàm khác nhỏ hơn được hàm này gọi đã được test thì chúng ta sẽ không bao giờ gặp trường hợp như thế này" - Tôi nghĩ lập luận này là cứt bò. Nếu đập tất cả mọi thứ nhỏ đến mức có thể làm một cái "unit test" hoàn toàn độc lập, nhỏ đến mức trivial như ở điểm (1) thì cái chúng ta có sẽ là x10 số lượng hàm ( Để giảm độ phức tạp từng hàm?) , x10 số lượng class (Để giảm số property trên mỗi class?) , x10 độ sâu call stack ( Hậu quả của việc xẻ nhỏ hàm) và x100 LOC mà không mang lại giá trị gì thật sự cả
Nhận định cá nhân: Nếu một developer thực sự nghĩ trước khi implement một thứ gì trong
cùng một lượng thời gian với một developer dùng thời gian đó để nghĩ và viết unit test thì tôi tin tưởng nhiều hơn vào chất lượng của người
nghĩ và không viết unit test. Thứ mà unit test mang lại là nó
ép developer phải nghĩ. Chất lượng code tốt hơn vì developer đó đã nghĩ kỹ trước khi implement, không phải vì bản thân việc viết Unit Test
Điểm tốt khác là sớm phát hiện breaking changes. Cái này thực sự là một điểm + lớn, không phải bàn cãi
Bỏ qua BDD.
Theo tôi Integration test không nhất thiết phải dính đến DB mới gọi là Integration. Unit test là test một unit nhỏ của code, có thể là 1 hàm, 1 class (hay gọi là 1 module/unit). Integration test là test cover mối liên hệ giữa các module với nhau, thông thường hay test xuyên unit và tới DB, nhưng quan điểm cá nhân là test giữa các Units với nhau.
Tôi xem qua ví dụ của bác, đầu tiên tôi nhận xét là class đó violate nhiều rules, ví dụ:
Single Responsibility
Đặt tên Builder là mis-leading, theo tôi nên là Factory.
Như có nói ở 1 comment khác là ngoài Unit Testing còn có nhiều quy trình đảm bảo chất lượng code như Code Review, và mục đích bài này chỉ muốn bàn về lý do + thái độ với tests (không phải tutorial).
StreamBuilder: có một loạt các property ở ctor/setter mục đích để build a stream, có 1 nhiệm vụ là tạo builder với các property đó (tất nhiên kèm theo validation). Builder chỗ này nó là raw logic, không quan tâm datasource nên có thể viết unit test bằng cách provide đủ property và expect result tương ứng với property đó. Ví dụ:
property A = 0 -> invalid
property A > 0, property B = 1 => expect có X stream với Max Bitrate là Y
StreamBuilderDirector: mục đích là fetch dữ liệu từ data source và pass to
StreamBuilder có thể viết unit test như sau:
Expect fetch dữ liệu db từ repository A (đoạn này thậm chí chỉ cần mock)
Expect đọc config từ file B (đoạn này thậm chí chỉ cần mock)
Client: Chính là cái Factory như code bác provided, Client rất đơn giản là tạo director, xài director tạo builder và build thành Stream. Lúc này cần phải viết Integration Test cho Client, mục đích là đảm bảo mình gọi đúng director và khi gọi build trả về kết quả expected. Có thể mock, có thể không nhưng the point là nó test through 2 units.
--------------------------------
Cheers!
Edit:
StreamBuilder có thể nhận props ở ctor hoặc qua setter, tuỳ style nhưng nguyên tắc vẫn là pure data.
Nếu bác xài RDBM thì cho cái test vào transaction ở setUp nhưng đừng commit transaction đó. Còn xài NoSQL thì seed db rồi delete cả db cho khoẻ. Cái Seed cũng quan trọng, bác đầu tư phần này đáng lắm.
Edit: kinh nghiệm là không nên re-use db cho nhiều test cases. Tôi đoán bác đang xài No-SQL nên gặp vấn đề này, 1 db cùng lắm nên xài cho 1 test suite, đừng nên xài chung quá nhiều. Có 2 lý do:
DB là lưu state, trong khi test nên là stateless, nếu lưu db mà không xoá sẽ có nhiều side-effect và maybe là flaky tests.
Tách db để run test multi-thread -> deploy nhanh hơn.
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à:
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.
testing front-end mình thấy có hai loại ( theo những gì mình thấy, hồi trước làm fullstack hơn một năm xong nhảy qua đội data-infra )
logic testing: các thuật toán hay hàm dùng để xử lý dữ liệu từ dạng này sang dạng khác, hoặc làm các công vụ không liên quan đến UI, có thể dùng jest/mocha
UI test nhanh nhất vẫn là xài mấy cái solution về visual testing nhé ví dụ Applitools. Commit xong lên review UI changes trên cross browsers, devices nếu thấy ko có issue thì UI ok thôi, time đâu check layout rồi responsive = auto đơn thuần sao chịu nổi
snake_case
Nếu việc trả về một giá trị của hàm trong unit test cần thiết thì không nên để kiểu trả về là
void đúng không mọi người?
tuanhpg
Bác nào review UT và TDD thực tế cho dev mobile đc k ạ. Cụ thể android ý
Love U So Much
tôi đang có kinh nghiệm migration 1 loạt app từ on-premise lên aws cloud. đổi db, đổi message queue. khoảng 500+ web app, còn gần 100 cái nữa là xong
ko có code test thì việc này ko thể làm được. rất may bên khách hàng là công ty mỹ, gần như đủ hết các loại test. dẫn đến việc của tôi làm rất dễ
có unit test -> test function, sql query, docker test, intergration test riêng
Tôi nghĩ #39 nói rất đúng. Thật sự tôi không rõ định nghĩa Unit Test của mỗi người là như thế nào để chúng ta có thể thảo luận được một cách tử tế về chủ đề này
(1) Nếu Unit Test nhắm tới các khối code nhỏ nhất như #1 nói đến hoặc như nhiều người "preach" thì đúng... nó sẽ rất "dễ" ... dễ đến mức Unit Testing không còn ý nghĩa gì nữa ?
Để có được mức độ Isolation như vậy thì Unit Test này phải test các khối code rất nhỏ, các phép toán như createSaltedPassword() hay CustomGraphGetNodeByPropertyValue()
(2) Tôi tin phần lớn các test chúng ta viết rơi vào trường hơp "
Integration Test / Integration Unit Test" khi đã có đủ các thể loại ban bệ (dependency) trong source code của bạn. Giả sử bạn có 1 hàm như thế này - từ Jellyfin
Nhìn thì có vẻ rất đơn giản - chỉ có 2 parameter. Tuy nhiên mỗi object được gửi vào có một
tấn property và bản thân hàm này cũng cho ra 1
tấn các kết quả khác nhau. Vậy chúng ta làm gì với nó?
Mock tất cả mọi thứ và test tất cả mọi thứ? ... well nhắc đến mock thì nó lại là một ổ sâu khác
Không test nó?
"Hàm trên chưa được viết tốt, khi chúng ta đã phân chia công việc một cách tử tế / đủ nhỏ để unit test thì sẽ không bao giờ xảy ra trường hợp như thế này, bản thân các hàm khác nhỏ hơn được hàm này gọi đã được test thì chúng ta sẽ không bao giờ gặp trường hợp như thế này" - Tôi nghĩ lập luận này là cứt bò. Nếu đập tất cả mọi thứ nhỏ đến mức có thể làm một cái "unit test" hoàn toàn độc lập, nhỏ đến mức trivial như ở điểm (1) thì cái chúng ta có sẽ là x10 số lượng hàm ( Để giảm độ phức tạp từng hàm?) , x10 số lượng class (Để giảm số property trên mỗi class?) , x10 độ sâu call stack ( Hậu quả của việc xẻ nhỏ hàm) và x100 LOC mà không mang lại giá trị gì thật sự cả
Nhận định cá nhân: Nếu một developer thực sự nghĩ trước khi implement một thứ gì trong
cùng một lượng thời gian với một developer dùng thời gian đó để nghĩ và viết unit test thì tôi tin tưởng nhiều hơn vào chất lượng của người
nghĩ và không viết unit test. Thứ mà unit test mang lại là nó
ép developer phải nghĩ. Chất lượng code tốt hơn vì developer đó đã nghĩ kỹ trước khi implement, không phải vì bản thân việc viết Unit Test
Điểm tốt khác là sớm phát hiện breaking changes. Cái này thực sự là một điểm + lớn, không phải bàn cãi
zzzz anh đi tin vào con người thay vì code là tôi thấy ko dc rồi
Lời nói của anh mâu thuẫn quá.
Anh tin vào người "nghĩ và ko viết UT", nhưng lại cho rằng UT khiến dev nghĩ tốt hơn.
Có thấy mâu thuẫn trong câu nói này ko
Nếu UT khiến dev nghĩ tốt hơn, vậy tại sao ko là vừa "UT vừa nghĩ", mà lại là "nghĩ mà ko viết UT"
Tôi tin code hơn là tin con người nhé.
Chưa kể con ng nghĩ tới mấy cũng có sai sót. Ko viết UT thì lấy gì mà đo lường dc là 1 ng dev đã nghĩ kỹ hay chưa.
UT rất hay, có chăng là ko thể giành quá nhiều thời gian cho nó, thì ít nhất cũng phải có các case cơ bản, corner case, abnormal case.
Các quy trình đưa ra để giảm thiểu lỗi của con người, UT cũng thế. Tôi còn ko tin tôi viết code thì anh lấy gì mà tin dc người khác?
Đấy là còn chưa kể các tiện lợi khác của UT khi maintain, update code v.v...
zzzz anh đi tin vào con người thay vì code là tôi thấy ko dc rồi
Lời nói của anh mâu thuẫn quá.
Anh tin vào người "nghĩ và ko viết UT", nhưng lại cho rằng UT khiến dev nghĩ tốt hơn.
Có thấy mâu thuẫn trong câu nói này ko
Nếu UT khiến dev nghĩ tốt hơn, vậy tại sao ko là vừa "UT vừa nghĩ", mà lại là "nghĩ mà ko viết UT"
Tôi tin code hơn là tin con người nhé.
Chưa kể con ng nghĩ tới mấy cũng có sai sót. Ko viết UT thì lấy gì mà đo lường dc là 1 ng dev đã nghĩ kỹ hay chưa.
UT rất hay, có chăng là ko thể giành quá nhiều thời gian cho nó, thì ít nhất cũng phải có các case cơ bản, corner case, abnormal case.
Các quy trình đưa ra để giảm thiểu lỗi của con người, UT cũng thế. Tôi còn ko tin tôi viết code thì anh lấy gì mà tin dc người khác?
Đấy là còn chưa kể các tiện lợi khác của UT khi maintain, update code v.v...
Vì người nghĩ và viết UT thì sẽ tốn thời gian cho việc viết và bớt thời gian cho việc nghĩ đó anh
Tôi chưa thấy có gì mâu thuẫn ở đây cả
Còn tôi cũng nói rõ lợi ích của UT khi có breaking change rồi... việc đấy không phải bàn cãi
Last edited:
Fire Of Heart
cái vấn đề của UT là mất thời gian thôi, nhưng nó sẽ đảm cho chất lượng.
Còn lợi ích thì ko đơn giản là chỉ khi breaking change, lấy gì để biết code 1 ng có chất lượng hay ko? Phải test đúng ko?
Giờ a đưa code để ng khác review, ko lẽ họ phải chạy tay từng test case để kiểm tra?
Vì họ đâu thể biết dc chất lượng của anh thế nào?
Nói đơn giản thế thôi.
Vì người nghĩ và viết UT thì sẽ tốn thời gian cho việc viết và bớt thời gian cho việc nghĩ đó anh
Tôi chưa thấy có gì mâu thuẫn ở đây cả
Còn tôi cũng nói rõ lợi ích của UT khi có breaking change rồi... việc đấy không phải bàn cãi
A phải biết là lỗi ở môi trường production nó thế nào, so với thời gian dev của a, cái nào tốn thời gian hơn
Đối với tôi code ko có test là code rác, tôi từ chối làm ngay từ đầu luôn.
A gặp tôi thì ko có thời gian nghĩ đâu. Task break nhỏ ra, ko bao giờ tôi để a làm cái task nào quá 1 tuần. Ko dc thì làm cái khác.
có ngoại lệ, nhưng task đó phải trên 3 tháng, và thời gian lại là vô biên
Tôi nghĩ #39 nói rất đúng. Thật sự tôi không rõ định nghĩa Unit Test của mỗi người là như thế nào để chúng ta có thể thảo luận được một cách tử tế về chủ đề này
(1) Nếu Unit Test nhắm tới các khối code nhỏ nhất như #1 nói đến hoặc như nhiều người "preach" thì đúng... nó sẽ rất "dễ" ... dễ đến mức Unit Testing không còn ý nghĩa gì nữa ?
Để có được mức độ Isolation như vậy thì Unit Test này phải test các khối code rất nhỏ, các phép toán như createSaltedPassword() hay CustomGraphGetNodeByPropertyValue()
(2) Tôi tin phần lớn các test chúng ta viết rơi vào trường hơp "
Integration Test / Integration Unit Test" khi đã có đủ các thể loại ban bệ (dependency) trong source code của bạn. Giả sử bạn có 1 hàm như thế này - từ Jellyfin
Nhìn thì có vẻ rất đơn giản - chỉ có 2 parameter. Tuy nhiên mỗi object được gửi vào có một
tấn property và bản thân hàm này cũng cho ra 1
tấn các kết quả khác nhau. Vậy chúng ta làm gì với nó?
Mock tất cả mọi thứ và test tất cả mọi thứ? ... well nhắc đến mock thì nó lại là một ổ sâu khác
Không test nó?
"Hàm trên chưa được viết tốt, khi chúng ta đã phân chia công việc một cách tử tế / đủ nhỏ để unit test thì sẽ không bao giờ xảy ra trường hợp như thế này, bản thân các hàm khác nhỏ hơn được hàm này gọi đã được test thì chúng ta sẽ không bao giờ gặp trường hợp như thế này" - Tôi nghĩ lập luận này là cứt bò. Nếu đập tất cả mọi thứ nhỏ đến mức có thể làm một cái "unit test" hoàn toàn độc lập, nhỏ đến mức trivial như ở điểm (1) thì cái chúng ta có sẽ là x10 số lượng hàm ( Để giảm độ phức tạp từng hàm?) , x10 số lượng class (Để giảm số property trên mỗi class?) , x10 độ sâu call stack ( Hậu quả của việc xẻ nhỏ hàm) và x100 LOC mà không mang lại giá trị gì thật sự cả
Nhận định cá nhân: Nếu một developer thực sự nghĩ trước khi implement một thứ gì trong
cùng một lượng thời gian với một developer dùng thời gian đó để nghĩ và viết unit test thì tôi tin tưởng nhiều hơn vào chất lượng của người
nghĩ và không viết unit test. Thứ mà unit test mang lại là nó
ép developer phải nghĩ. Chất lượng code tốt hơn vì developer đó đã nghĩ kỹ trước khi implement, không phải vì bản thân việc viết Unit Test
Điểm tốt khác là sớm phát hiện breaking changes. Cái này thực sự là một điểm + lớn, không phải bàn cãi
cái vấn đề của UT là mất thời gian thôi, nhưng nó sẽ đảm cho chất lượng.
Còn lợi ích thì ko đơn giản là chỉ khi breaking change, lấy gì để biết code 1 ng có chất lượng hay ko? Phải test đúng ko?
Giờ a đưa code để ng khác review, ko lẽ họ phải chạy tay từng test case để kiểm tra?
Vì họ đâu thể biết dc chất lượng của anh thế nào?
Nói đơn giản thế thôi.
Đúng. Và không phải lúc nào chúng ta cũng có thời gian phải không nào. Nếu dev sẵn sang bỏ ra một vài tiếng để nghĩ, thêm một vài tiếng cho viết UT và thêm một vài phút cho implementation thì chắc chắn mọi thứ sẽ rất tốt đẹp rồi. Tôi rất ủng hộ UT nếu nó phù hợp với tính chất dự án và tổng lợi ích nó mang lại lớn hơn tổng công sức bỏ ra. Chuyện
không phải lúc nào cũng thế Tôi không nghĩ để UT
"drive" development luôn là một ý hay
A phải biết là lỗi ở môi trường production nó thế nào, so với thời gian dev của a, cái nào tốn thời gian hơn
Đối với tôi code ko có test là code rác, tôi từ chối làm ngay từ đầu luôn.
A gặp tôi thì ko có thời gian nghĩ đâu. Task break nhỏ ra, ko bao giờ tôi để a làm cái task nào quá 1 tuần. Ko dc thì làm cái khác.
có ngoại lệ, nhưng task đó phải trên 3 tháng, và thời gian lại là vô biên
Nếu anh có suy nghĩ như thế thì khá tiếc khi nhiều dự án thú vị không có cơ hội nhận được sự đóng góp của anh rồi...
Nhiều các repo public có cộng đồng lớn, đang active development như Jellyfin tôi có nhắc đến ở trên hay webtorrent họ không hề làm TDD, lượng test cũng cực kỳ hạn chế. Điều đó không cản trở họ làm ra một dự án tốt
All-in-all như tôi đã nói trong bài viết đầu tiên, tôi không advocate việc "Đừng TDD, đừng viết UT, nó quá vô bổ và lãng phí"
Cái tôi muốn nói là
"TDD không phải là chìa khóa vạn năng cho một project tốt, ngược lại, nó sẽ kéo project của bạn đi xuống nếu không nhận thức được rõ những vấn đề mà nó mang lại"
Đúng. Và không phải lúc nào chúng ta cũng có thời gian phải không nào. Nếu dev sẵn sang bỏ ra một vài tiếng để nghĩ, thêm một vài tiếng cho viết UT và thêm một vài phút cho implementation thì chắc chắn mọi thứ sẽ rất tốt đẹp rồi. Tôi rất ủng hộ UT nếu nó phù hợp với tính chất dự án và tổng lợi ích nó mang lại lớn hơn tổng công sức bỏ ra. Chuyện
không phải lúc nào cũng thế Tôi không nghĩ để UT
"drive" development luôn là một ý hay
Nếu anh có suy nghĩ như thế thì khá tiếc khi nhiều dự án thú vị không có cơ hội nhận được sự đóng góp của anh rồi...
Nhiều các repo public có cộng đồng lớn, đang active development như Jellyfin tôi có nhắc đến ở trên hay webtorrent họ không hề làm TDD, lượng test cũng cực kỳ hạn chế. Điều đó không cản trở họ làm ra một dự án tốt
All-in-all như tôi đã nói trong bài viết đầu tiên, tôi không advocate việc "Đừng TDD, đừng viết UT, nó quá vô bổ và lãng phí"
Cái tôi muốn nói là
"TDD không phải là chìa khóa vạn năng cho một project tốt, ngược lại, nó sẽ kéo project của bạn đi xuống nếu không nhận thức được rõ những vấn đề mà nó mang lại"
Tôi hỏi thật, anh làm việc kiểu "ko có thời gian viết UT" chiếm bao nhiêu % trong các dự án của anh (trừ trường hợp anh ko phải là 1 startup nào đó và trong giai đoạn cần chạy đua để release).
Nếu anh bảo > 30% thì tôi nghĩ anh nên cân nhắc 1 vài thứ về việc quản lý thời gian của leader/PM dự án, hoặc của anh!
Mà kể cả qua giai đoạn gấp rút đó thì sau này cũng phải viết UT để bù vào thôi
Tôi đồng ý quan điểm trên kia, code ko có UT thì quality thấp, ko gì đảm bảo dc chất lượng release.
Chưa kể hôm nay tiết kiêm mấy tiếng viết UT, ngày sau lại tốn thêm thời gian để fix bug, maintain thôi.
Bổ sung thêm 1 cái: Việc anh dẫn chứng cái repo kia là ko hợp lý, tôi có thể quăng ra 10 cái repo có UT đầy đủ cho anh coi, anh tin ko? Thế muốn theo repo nào
Đúng là UT ko phải vạn năng, và chất lượng của 1 project nó chịu ảnh hưởng của rất nhiều yếu tố.
Chính vì thế nên mới cần UT để đảm bảo chất lượng 1 phần nào đó.
Last edited:
cuongphungvng
Unit Test hay test code nói chung là tốt cho
dài hạn chứ ngắn hạn thì không. Cho nên các thím tranh luận thì xem lại có đúng góc nhìn chưa nhé ?
Tôi hỏi thật, anh làm việc kiểu "ko có thời gian viết UT" chiếm bao nhiêu % trong các dự án của anh (trong trường hợp anh ko phải là 1 startup nào đó và trong giai đoạn cần chạy đua để release)
Nếu anh bảo > 30% thì tôi nghĩ anh
nên cân nhắc 1 vài thứ về việc quản lý thời gian của leader/PM dự án, hoặc của anh!
Cũng có thể
tính chất công việc mỗi người khác nhau mà. Vì thế tôi mới có đóng góp hơi trái chiều ở đây, từ góc nhìn của tôi
Có thể các bạn ở đây trình độ cao hơn hoặc mặt bằng chung team trình độ tốt hơn nên không gặp phải những khó khăn mà tôi gặp phải... đó cũng là chuyện bình thường thôi