Ở bài viết Introduction to Unit Test đã nói lên được sự quan trọng của Unit Test
. Nhưng trước khi áp dụng được Unit Test
vào dự án hiện tại hoặc tương lai thì ta cần phải có một “bước đệm” đó học cách viết Testable Code
(là Production Code
cho phép thực hiện Unit Test
dễ dàng). Việc này theo mình là khó khăn và vì thế bài viết này sẽ tổng hợp một số kinh nghiệm của mình để giúp bạn thực hiện việc này dễ dàng hơn.
Trước khi đọc nội dung bài viết. Mình mong bạn hãy đọc những bài sau để có kiến thức nền cho bài viết này:
Write a simple Unit
Khi ta viết một Unit
[1] đơn giản sẽ giúp ích việc Unit Test
trở nên đơn giản vì các yếu tố sau:
- Số
Test Case
choUnit
ít hơn. - Các dependency mà
Unit
phụ thuộc cũng ít hơn từ đó việc chuẩn bị hoặcMock
[2] cho cácTest Case
cũng trở nên đơn giản. - Hạn chế việc miss
Test Case
.
Vậy đối với những yêu cầu phức tạp ta cần làm gì?
Câu trả lời là tách yêu cầu trên thành các yêu cầu nhỏ hơn và giải quyết từng yêu cầu. Giống việc áp dụng chiến thuật “chia để trị” vậy đó ;))
Separation Between Logic and Presentation
Việc tách biệt giữa code xử lý logic và code lưu trữ, hiển thị hoặc call API là việc bắt buộc khi muốn dự án của ta có thể dễ dàng bảo trì và mở rộng về sau. Ý tưởng tách biệt này là ý tưởng chính trong các mô hình MVC
, MVVM
…
Vì thế mà ngay cả khi chiến lược đảm bảo chất lượng của dự án không yêu cầu Unit Test
thì ta cũng nên thực hiện việc này.
Ngoài ra việc tách biệt này cũng sẽ giúp cho việc đảm bảo Consistency Unit
ở phần sau được thực hiện.
Consistency Unit
Tính Consistency
ở đây được biểu hiện qua việc với cùng một input thì Unit
luôn trả về duy nhất một output. Việc này giúp việc kiểm tra input và output như mình đề cập ở Introduction to Unit Test có thể được thực hiện.
Một số trường hợp không thể thực hiện tính Consistency
như:
- Hàm liên quan tới
random
. - Các hàm liên quan tới thời gian hiện tại như tính tuổi hiện tại từ ngày sinh, số ngày chênh lệch ở một thời điểm nào đó so với hiện tại…
- Các lời gọi tới external system như DB, API, Cache…
- Các lời gọi liên quan tới context như thread context, security context, scope context…
- …
Và mình đề xuất 2 cách giải quyết các trường hợp trên như sau:
- Tách code logic và code inconsistency kia thành 2 phần riêng biệt. Lúc này input của code logic sẽ là output của code inconsistency. Và ta chỉ
Unit Test
phần code logic. - Tương tự #1 khi tách ra 2 phần riêng biệt. Áp dụng
IoC/DI
, lúc này code logic làClient
, code inconsistency làService Implement
. Và cũng chỉ cầnUnit Test
phần code logic với việcMock
2 phần code inconsistency.
Apply Ioc/DI
Việc áp dụng IoC/DI
giúp ta có thể dễ dàng Mock
2 các phụ thuộc cho các class
cần Unit Test
một cách dễ dàng. Ngoài ra việc áp dụng IoC/DI
cũng sẽ giúp thỏa được nguyên lý DIP
ở phần dưới.
Apply S.O.L.I.D principles
Việc áp dụng S.O.L.I.D
đặc biệt có lợi cho việc viết Unit Test
vì:
Single responsibility priciple
: giữ cho cácclass
chứa cácUnit
cần test đơn giản nhất có thể. Từ đó số lượngTest Case
choclass
được giảm xuống.Open/Closed principle
: Giúp ta không phải viết lại cácTest Case
cho phần code cũ khi cần mở rộng tính năng.Liskov substitution principe
: Đây là nguyên lý khó giải thích được sự liên hệ của nó vớiTestable Code
:(. Ở đây mình giả sử class A phụ thuộc class B, ta đãUnit Test
class A và class B rồi. Vậy khi mình viết thêm một class C kế thừa class B và thay thế class B bằng class C lúc này ta chỉ cầnUnit Test
thêm class C mà không cần phảiUnit Test
lại class A nếu thỏaLSP
. Nếu chương trình thỏa nguyên lý này. Ta sẽ không cần lạiUnit Test
nhữngclass
đã đượcInterface segregation principle
: nguyên lý này đi kèm vớiSRP
sẽ giúp đơn giản hóa việcUnit Test
cho cácclass
implement cácinterface
thỏa nguyên lý này.Dependency inversion principle
: Đây là nguyên lý quan trọng để có thể viếtTestable Code
được. Nếu bạn đã áp dụngIoC/DI
như phần trên thì nguyên lý này cũng tự động thỏa.
Kết: Để viết được Testable Code
không phải dễ nhưng khi đã làm được thì xem như bạn có thể bước một chân vào thế giới của các developer “xịn” rồi đó. Ngoài ra viết Testable Code
nên thực hiện càng sớm càng tốt vì sẽ tốn rất nhiều thời gian, công sức và rủi ro để chuyển code thường sang Testable Code
.
Cảm ơn các bạn đã đọc tới đây và mong các bạn sẽ thích bài viết này.
Reference articles
- Guide To Writing Testable Code
- 4 Properties of Highly Testable Code
- Writing Testable Code
- Why consistency is one of the top indicators of good code
Unit
ở đây đối với lập trình hướng đối tượng làmethod
, đối với lập trình hàm làfunction
, đối với lập trình thủ tục làprocedure
.Mock
hiểu đơn giản là cách để giả lập hành vi của cácclass
,method
bên ngoàiUnit
đang đượcUnit Test
. Còn chi tiết thì ở Use Mock to make Unit Test easy ↩︎ ↩︎