JavaRush /Blog Java /Random-VI /TDD và kiểm tra đơn vị là gì [bản dịch]
Dr-John Zoidberg
Mức độ
Марс

TDD và kiểm tra đơn vị là gì [bản dịch]

Xuất bản trong nhóm
Bài viết này được chuyển thể từ một chương trong cuốn sách Hướng dẫn nghề nghiệp phần mềm hoàn chỉnh. Tác giả của nó, John Sonmez, viết nó và đăng một số chương trên trang web của mình.
TDD và unit test là gì [dịch] - 1

Bảng thuật ngữ ngắn dành cho người mới bắt đầu

Kiểm thử đơn vị hoặc kiểm thử đơn vị là một quy trình trong lập trình cho phép bạn kiểm tra tính chính xác của từng mô-đun riêng lẻ trong mã nguồn của chương trình. Ý tưởng là viết các bài kiểm tra cho mọi hàm hoặc phương thức không tầm thường. Kiểm tra hồi quy là tên chung cho tất cả các loại kiểm thử phần mềm nhằm phát hiện lỗi trong các khu vực đã được kiểm tra của mã nguồn. Những lỗi như vậy - sau khi thực hiện các thay đổi đối với chương trình, một thứ gì đó lẽ ra vẫn tiếp tục hoạt động lại ngừng hoạt động - được gọi là lỗi hồi quy. Kết quả màu đỏ, thất bại - thử nghiệm thất bại. Sự khác biệt giữa kết quả mong đợi và kết quả thực tế. Kết quả xanh, đạt - kết quả xét nghiệm dương tính. Kết quả thực tế không khác gì những gì đạt được. ***
TDD và unit test là gì [dịch] - 2
Tôi có một mối quan hệ rất lẫn lộn với Phát triển dựa trên thử nghiệm (TDD) và thử nghiệm đơn vị, từ yêu đến ghét và ngược lại. Tôi là một người hâm mộ cuồng nhiệt, đồng thời là một người hoài nghi đáng ngờ về việc sử dụng điều này cũng như những “phương pháp hay nhất” khác. Lý do cho thái độ của tôi là dựa trên thực tế là một vấn đề nghiêm trọng đã xuất hiện trong quá trình phát triển phần mềm: các nhà phát triển và đôi khi là người quản lý, chỉ sử dụng một số công cụ và phương pháp nhất định vì chúng thuộc về “các phương pháp hay nhất”. Lý do thực sự cho việc sử dụng chúng vẫn chưa rõ ràng. Một ngày nọ, tôi bắt đầu làm việc trên một dự án nhất định và trong quá trình đó, tôi được thông báo rằng chúng tôi sẽ sửa đổi mã được bao phủ bởi một số lượng lớn các bài kiểm tra đơn vị. Không đùa đâu, có khoảng 3000 người trong số họ. Đây thường là một dấu hiệu tốt, một tín hiệu cho thấy các nhà phát triển đang sử dụng các phương pháp tiên tiến. Mã theo cách tiếp cận này thường có cấu trúc nhất định và nó dựa trên một kiến ​​trúc được cân nhắc kỹ lưỡng. Nói một cách dễ hiểu, sự hiện diện của các bài kiểm tra khiến tôi rất vui, nếu chỉ vì nó có nghĩa là khiến công việc cố vấn cho các lập trình viên của tôi trở nên dễ dàng hơn. Vì chúng tôi đã có các bài kiểm tra đơn vị nên tất cả những gì tôi phải làm là kết nối nhóm phát triển để hỗ trợ họ và bắt đầu viết mã của riêng mình. Tôi đã mở IDE (môi trường phát triển tích hợp) và tải dự án.
TDD và unit test là gì [dịch] - 3
Đó là một dự án lớn! Tôi tìm thấy một thư mục có nhãn "kiểm tra đơn vị". “Tuyệt,” tôi nghĩ. - Hãy khởi động nó và xem điều gì sẽ xảy ra. Chỉ mất vài phút, và thật ngạc nhiên, tất cả các bài kiểm tra đều vượt qua, mọi thứ đều có màu xanh ( "green" là kết quả dương tính của bài kiểm tra. Tín hiệu cho thấy mã đang hoạt động như mong đợi. Màu đỏ biểu thị "thất bại" hoặc thất bại, sau đó có trường hợp mã không hoạt động chính xác - ghi chú của người dịch ). Tất cả họ đều vượt qua bài kiểm tra. Ngay lúc đó, sự hoài nghi trong tôi bừng tỉnh. Tại sao ba nghìn bài kiểm tra đơn vị lại được thực hiện cùng một lúc - và cho kết quả khả quan? Trong quá trình thực hành lâu dài của mình, tôi không thể nhớ được thời điểm nào tôi bắt đầu làm việc trên một dự án mà không có một bài kiểm tra đơn vị âm nào trong mã. Phải làm gì? Kiểm tra thủ công! ChY chọn một bài kiểm tra ngẫu nhiên, không phải bài kiểm tra rõ ràng nhất, nhưng ngay lập tức biết rõ anh ấy đang kiểm tra cái gì. Nhưng khi thực hiện nó, tôi nhận thấy một điều vô lý: bài kiểm tra không có bất kỳ so sánh nào với kết quả mong đợi (khẳng định)! Đó là, trong thực tế không có gì được kiểm tra cả ! Có một số bước nhất định trong bài kiểm tra, chúng đã được thực hiện, nhưng khi kết thúc bài kiểm tra, nơi anh ta nên so sánh kết quả thực tế và kết quả mong đợi, lại không có sự kiểm tra nào. "Bài kiểm tra" không kiểm tra bất cứ điều gì. Tôi đã mở một bài kiểm tra khác. Thậm chí tốt hơn: toán tử so sánh với kết quả đã được nhận xét. Rực rỡ! Đây là một cách tuyệt vời để vượt qua bài kiểm tra, chỉ cần nhận xét mã khiến nó bị lỗi. Tôi đã kiểm tra một bài kiểm tra khác, rồi một bài kiểm tra khác... Không ai trong số họ kiểm tra bất cứ điều gì. Ba ngàn bài kiểm tra, và tất cả chúng đều hoàn toàn vô dụng. Có một sự khác biệt rất lớn giữa việc viết bài kiểm tra đơn vị và hiểu bài kiểm tra đơn vị cũng như phát triển theo hướng kiểm tra (TDD).

Kiểm tra đơn vị là gì?

TDD và unit test là gì [dịch] - 4
Ý tưởng cơ bản của thử nghiệm đơn vị là viết các bài kiểm tra để kiểm tra “đơn vị” mã nhỏ nhất. Các bài kiểm tra đơn vị thường được viết bằng cùng ngôn ngữ lập trình với mã nguồn của ứng dụng. Chúng được tạo trực tiếp để kiểm tra mã này. Nghĩa là, kiểm thử đơn vị là mã kiểm tra tính chính xác của mã khác. Tôi sử dụng từ “kiểm tra” khá tự do trong ngữ cảnh này, bởi vì các bài kiểm tra đơn vị theo một nghĩa nào đó không phải là bài kiểm tra. Họ không trải nghiệm bất cứ điều gì. Ý tôi là khi bạn chạy một bài kiểm thử đơn vị, bạn thường không thấy rằng một số mã không hoạt động. Bạn phát hiện ra điều này khi viết bài kiểm tra vì bạn sẽ thay đổi mã cho đến khi bài kiểm tra chuyển sang màu xanh. Có, mã có thể thay đổi sau đó và thử nghiệm của bạn có thể thất bại. Vì vậy, theo nghĩa này, bài kiểm tra đơn vị là bài kiểm tra hồi quy. Bài kiểm tra đơn vị không giống như bài kiểm tra thông thường trong đó bạn có một số bước bạn sẽ thực hiện và xem liệu phần mềm có hoạt động chính xác hay không. Trong quá trình viết bài kiểm thử đơn vị, bạn sẽ khám phá xem mã có thực hiện những gì được yêu cầu hay không và bạn sẽ thay đổi mã cho đến khi bài kiểm tra vượt qua.
TDD và unit test là gì [dịch] - 5
Tại sao không viết một bài kiểm tra đơn vị và kiểm tra xem nó có vượt qua không? Nếu bạn nghĩ về nó theo cách này, thì các bài kiểm tra đơn vị sẽ trở thành một loại yêu cầu tuyệt đối nào đó đối với một số mô-đun mã nhất định ở mức rất thấp. Bạn có thể coi bài kiểm thử đơn vị là một thông số kỹ thuật tuyệt đối . Kiểm thử đơn vị xác định rằng trong những điều kiện này, với tập hợp đầu vào cụ thể này, sẽ có đầu ra mà bạn sẽ nhận được từ đơn vị mã này. Kiểm thử đơn vị thực sự xác định đơn vị mã mạch lạc nhỏ nhất, trong hầu hết các ngôn ngữ lập trình - ít nhất là ngôn ngữ hướng đối tượng - là một lớp.

Đôi khi cái gì được gọi là kiểm thử đơn vị?

TDD và unit test là gì [dịch] - 6
Kiểm thử đơn vị thường bị nhầm lẫn với kiểm thử tích hợp. Một số "kiểm tra đơn vị" kiểm tra nhiều lớp hoặc kiểm tra các đơn vị mã lớn. Rất nhiều nhà phát triển khẳng định rằng họ viết các bài kiểm thử đơn vị trong khi thực tế họ viết các bài kiểm thử hộp trắng cấp thấp. Đừng tranh cãi với những kẻ này. Chỉ cần biết rằng họ thực sự viết các bài kiểm tra tích hợp và các bài kiểm tra đơn vị thực sự kiểm tra đơn vị mã nhỏ nhất tách biệt với các phần khác. Một điều khác thường được gọi là kiểm thử đơn vị là kiểm thử đơn vị mà không kiểm tra giá trị mong đợi. Nói cách khác, các bài kiểm tra đơn vị không thực sự kiểm tra bất cứ điều gì. Bất kỳ thử nghiệm nào, dù được thống nhất hay không, đều phải bao gồm một số loại xác minh - chúng tôi gọi đó là kiểm tra kết quả thực tế so với kết quả mong đợi. Chính sự hòa hợp này quyết định liệu bài kiểm tra có thành công hay thất bại. Một bài kiểm tra luôn vượt qua là vô ích. Một bài kiểm tra luôn thất bại là vô ích.

Giá trị của việc kiểm tra đơn vị

Tại sao tôi là người đam mê thử nghiệm đơn vị? Tại sao gọi thử nghiệm tổng quát là có hại, bao gồm thử nghiệm không phải khối nhỏ nhất tách biệt với mã khác, mà là một đoạn mã lớn hơn, “thử nghiệm đơn vị”? Vấn đề là gì nếu một số bài kiểm tra của tôi không so sánh kết quả nhận được và kết quả mong đợi? Ít nhất họ thực thi mã. Tôi sẽ cố gắng giải thích.
TDD và unit test là gì [dịch] - 7
Có hai lý do chính để thực hiện kiểm thử đơn vị. Đầu tiên là cải thiện thiết kế mã. Hãy nhớ tôi đã nói rằng thử nghiệm đơn vị không thực sự là thử nghiệm? Khi bạn viết các bài kiểm tra đơn vị phù hợp, bạn buộc mình phải tách biệt đơn vị mã nhỏ nhất. Những nỗ lực này sẽ khiến bạn phát hiện ra các vấn đề trong chính cấu trúc của mã. Bạn có thể thấy rất khó để tách lớp kiểm tra và không bao gồm các phần phụ thuộc của nó và điều này có thể khiến bạn nhận ra rằng mã của bạn được liên kết quá chặt chẽ. Bạn có thể thấy rằng chức năng cốt lõi mà bạn đang thử nghiệm trải rộng trên nhiều mô-đun, khiến bạn tin rằng mã của mình không đủ mạch lạc. Khi bạn ngồi viết một bài kiểm thử đơn vị, bạn có thể đột nhiên phát hiện ra (và tin tôi đi, điều đó xảy ra!) rằng bạn không biết đoạn mã đó phải làm gì. Theo đó, bạn sẽ không thể viết bài kiểm tra đơn vị cho nó. Và tất nhiên, bạn có thể tìm thấy một lỗi thực sự trong quá trình triển khai mã, vì kiểm tra đơn vị buộc bạn phải suy nghĩ sáng tạo và kiểm tra các nhóm đầu vào khác nhau mà bạn có thể chưa xem xét.
TDD và unit test là gì [dịch] - 8
Nếu bạn tuân thủ nghiêm ngặt quy tắc “kiểm tra đơn vị mã nhỏ nhất tách biệt với các đơn vị khác” khi tạo các bài kiểm tra đơn vị, bạn chắc chắn sẽ tìm thấy tất cả các loại vấn đề với mã đó và thiết kế của các mô-đun đó. Trong vòng đời phát triển phần mềm, kiểm thử đơn vị mang tính chất đánh giá hơn là hoạt động kiểm thử. Mục tiêu chính thứ hai của kiểm thử đơn vị là tạo ra một bộ kiểm thử hồi quy tự động có thể hoạt động như một đặc tả cấp thấp về hành vi của phần mềm. Nó có nghĩa là gì? Khi nhào bột không bị vỡ. Từ quan điểm này, bài kiểm tra đơn vị là bài kiểm tra, cụ thể hơn là bài kiểm tra hồi quy. Tuy nhiên, mục đích của thử nghiệm đơn vị không chỉ đơn giản là xây dựng các thử nghiệm hồi quy. Trong thực tế, các bài kiểm tra đơn vị hiếm khi phát hiện ra các hồi quy, vì một thay đổi đối với đơn vị mã mà bạn đang kiểm tra hầu như luôn chứa các thay đổi đối với chính bài kiểm tra đơn vị đó. Kiểm tra hồi quy hiệu quả hơn nhiều ở cấp độ cao hơn, khi mã được kiểm tra dưới dạng "hộp đen", vì ở cấp độ này cấu trúc bên trong của mã có thể được thay đổi, trong khi hành vi bên ngoài dự kiến ​​sẽ giữ nguyên. Unit test lần lượt kiểm tra cấu trúc bên trong để khi cấu trúc đó thay đổi thì unit test không bị lỗi. Chúng trở nên không sử dụng được và bây giờ cần phải thay đổi, vứt đi hoặc viết lại. Bây giờ bạn đã biết nhiều hơn về mục đích thực sự của thử nghiệm đơn vị so với nhiều nhà phát triển phần mềm kỳ cựu.

Phát triển dựa trên thử nghiệm (TDD) là gì?

TDD và unit test là gì [dịch] - 9
Trong quá trình phát triển phần mềm, một đặc tả tốt có giá trị như vàng. Cách tiếp cận TDD là trước khi bạn viết bất kỳ mã nào, trước tiên bạn phải viết một bài kiểm tra sẽ đóng vai trò là thông số kỹ thuật, nghĩa là xác định mã sẽ làm gì. Đây là một khái niệm phát triển phần mềm cực kỳ hiệu quả nhưng nó thường bị lạm dụng. Thông thường, phát triển dựa trên thử nghiệm có nghĩa là sử dụng các thử nghiệm đơn vị để hướng dẫn tạo mã ứng dụng. Nhưng trên thực tế, cách tiếp cận này có thể được áp dụng ở mọi cấp độ. Tuy nhiên, trong bài viết này, chúng tôi sẽ giả định rằng chúng tôi đang sử dụng thử nghiệm đơn vị cho ứng dụng của mình. Cách tiếp cận TDD đảo ngược mọi thứ và thay vì viết mã trước rồi viết bài kiểm tra đơn vị để kiểm tra mã đó, bạn viết bài kiểm tra đơn vị trước rồi viết mã để làm cho bài kiểm tra đó trở nên xanh. Bằng cách này, việc kiểm tra đơn vị sẽ “thúc đẩy” sự phát triển mã. Quá trình này được lặp đi lặp lại nhiều lần. Bạn viết một bài kiểm thử khác nhằm xác định nhiều chức năng hơn về những gì mã nên làm. Sau đó, bạn viết và sửa đổi mã để đảm bảo quá trình kiểm tra hoàn tất thành công. Khi bạn có kết quả màu xanh lá cây, bạn bắt đầu tái cấu trúc mã, nghĩa là tái cấu trúc hoặc dọn dẹp mã để mã ngắn gọn hơn. Chuỗi quy trình này thường được gọi là "Red-Green-Refactoring" vì đầu tiên thử nghiệm đơn vị thất bại (màu đỏ), sau đó mã được viết để thích ứng với thử nghiệm, đảm bảo nó thành công (màu xanh lá cây) và cuối cùng mã được tối ưu hóa ( tái cấu trúc). .

Mục tiêu của TDD là gì?

TDD và unit test là gì [dịch] - 10
Phát triển dựa trên thử nghiệm (TDD), giống như thử nghiệm đơn vị, có thể được sử dụng không chính xác. Rất dễ gọi những gì bạn làm là "TDD" và thậm chí làm theo cách thực hành mà không hiểu tại sao bạn lại làm như vậy. Giá trị lớn nhất của TDD là các thử nghiệm được thực hiện để tạo ra các thông số kỹ thuật chất lượng. TDD về cơ bản là thực hành viết các thông số kỹ thuật chính xác có thể được kiểm tra tự động trước khi viết mã. Các bài kiểm tra là thông số kỹ thuật tốt nhất vì chúng không nói dối. Họ sẽ không nói cho bạn biết sau hai tuần dày vò với mật mã “đó không phải ý tôi chút nào”. Các bài kiểm tra, khi được viết chính xác, sẽ đạt hoặc không đạt. Các thử nghiệm chỉ ra rõ ràng điều gì sẽ xảy ra trong những trường hợp nhất định. Vì vậy, mục tiêu của TDD là cung cấp cho chúng ta sự hiểu biết đầy đủ về những gì chúng ta cần triển khai trước khi bắt đầu triển khai nó. Nếu bạn đang bắt đầu với TDD và không thể hiểu bài kiểm tra sẽ kiểm tra cái gì, thì bạn cần đặt thêm câu hỏi. Một vai trò quan trọng khác của TDD là bảo toàn và tối ưu hóa mã. Bảo trì mã rất tốn kém. Tôi thường nói đùa rằng lập trình viên giỏi nhất là người viết đoạn mã ngắn nhất có thể giải quyết được vấn đề nào đó. Hoặc thậm chí là người chứng minh rằng vấn đề này không cần phải giải quyết và do đó loại bỏ hoàn toàn mã, vì chính lập trình viên này đã tìm ra cách phù hợp để giảm số lượng lỗi và giảm chi phí duy trì ứng dụng. Bằng cách sử dụng TDD, bạn có thể hoàn toàn chắc chắn rằng mình không viết bất kỳ mã không cần thiết nào vì bạn sẽ chỉ viết mã để vượt qua các bài kiểm tra. Có một nguyên tắc phát triển phần mềm tên là YAGNI (bạn sẽ không cần nó). TDD ngăn chặn YAGNI.

Quy trình làm việc phát triển theo hướng thử nghiệm (TDD) điển hình

TDD và unit test là gì [dịch] - 11
Hiểu ý nghĩa của TDD từ quan điểm học thuật thuần túy là điều khó khăn. Vì vậy, hãy xem một ví dụ về phiên TDD. Hãy tưởng tượng bạn đang ngồi xuống bàn làm việc và nhanh chóng phác thảo những gì bạn nghĩ sẽ là thiết kế cấp cao cho một tính năng cho phép người dùng đăng nhập vào ứng dụng và thay đổi mật khẩu nếu họ quên. Bạn quyết định rằng bạn sẽ bắt đầu với việc triển khai chức năng đăng nhập đầu tiên, tạo một lớp sẽ xử lý tất cả logic cho quá trình đăng nhập. Bạn mở trình soạn thảo yêu thích của mình và tạo một bài kiểm tra đơn vị có tên "Đăng nhập trống ngăn người dùng đăng nhập." Bạn viết mã kiểm thử đơn vị để tạo một phiên bản đầu tiên của lớp Đăng nhập (mà bạn chưa tạo). Sau đó, bạn viết mã để gọi một phương thức trong lớp Đăng nhập chuyển một tên người dùng và mật khẩu trống. Cuối cùng, bạn viết séc so với kết quả mong đợi, kiểm tra xem người dùng có thông tin đăng nhập trống có thực sự chưa đăng nhập hay không. Bạn đang cố chạy thử nghiệm nhưng nó thậm chí không được biên dịch vì bạn không có lớp Đăng nhập. Bạn khắc phục tình trạng này và tạo một lớp Đăng nhập cùng với một phương thức trong lớp đó để đăng nhập và một phương thức khác để kiểm tra trạng thái của người dùng xem họ đã đăng nhập chưa. Cho đến nay bạn vẫn chưa triển khai chức năng của lớp này và phương thức chúng tôi cần. Bạn chạy thử nghiệm vào thời điểm này. Bây giờ nó biên dịch, nhưng ngay lập tức thất bại.
TDD và unit test là gì [dịch] - 12
Bây giờ bạn quay lại mã và triển khai chức năng để vượt qua bài kiểm tra. Trong trường hợp của chúng tôi, điều này có nghĩa là chúng tôi sẽ nhận được kết quả: “người dùng chưa đăng nhập”. Bạn chạy lại bài kiểm tra và bây giờ nó đã thành công. Hãy chuyển sang bài kiểm tra tiếp theo. Bây giờ hãy tưởng tượng rằng bạn cần viết một bài kiểm tra có tên "Người dùng đã đăng nhập nếu họ nhập tên người dùng và mật khẩu hợp lệ." Bạn viết một bài kiểm tra đơn vị để khởi tạo lớp Đăng nhập và cố gắng đăng nhập bằng tên người dùng và mật khẩu. Trong bài kiểm tra đơn vị, bạn viết một câu lệnh rằng lớp Đăng nhập phải trả lời có cho câu hỏi liệu người dùng đã đăng nhập hay chưa. Bạn chạy thử nghiệm mới này và tất nhiên nó thất bại vì lớp Đăng nhập của bạn luôn trả về rằng người dùng chưa đăng nhập. Bạn quay lại lớp Đăng nhập của mình và triển khai một số mã để xác minh rằng người dùng đã đăng nhập. Trong trường hợp này, bạn sẽ phải tìm cách tách mô-đun này. Hiện tại, cách dễ nhất để thực hiện việc này là mã hóa cứng tên người dùng và mật khẩu bạn đã sử dụng trong thử nghiệm của mình và nếu chúng khớp nhau thì trả về kết quả "người dùng đã đăng nhập". Bạn thực hiện thay đổi này, chạy cả hai bài kiểm tra và cả hai đều vượt qua. Hãy chuyển sang bước cuối cùng: bạn xem mã được tạo và tìm cách sắp xếp lại và đơn giản hóa nó. Vì vậy, thuật toán TDD là:
  1. Đã tạo một bài kiểm tra.
  2. Chúng tôi đã viết mã cho bài kiểm tra này.
  3. Đã tái cấu trúc mã.

kết luận

TDD và unit test là gì [dịch] - 13
Đó là tất cả những gì tôi muốn nói với bạn về thử nghiệm đơn vị và TDD ở giai đoạn này. Trên thực tế, có rất nhiều khó khăn liên quan đến việc cố gắng tách biệt các mô-đun mã, vì mã có thể rất phức tạp và khó hiểu. Rất ít lớp tồn tại hoàn toàn biệt lập. Thay vào đó, chúng có các phần phụ thuộc, và những phần phụ thuộc đó có các phần phụ thuộc, v.v. Để giải quyết những tình huống như vậy, chuyên gia TDD sử dụng các mô hình giả, giúp cô lập các lớp riêng lẻ bằng cách thay thế các đối tượng trong các mô-đun phụ thuộc. Bài viết này chỉ là giới thiệu tổng quan và có phần đơn giản hóa về unit testing và TDD, chúng ta sẽ không đi sâu vào chi tiết về các module giả và các kỹ thuật TDD khác. Ý tưởng là cung cấp cho bạn các khái niệm và nguyên tắc cơ bản về TDD và kiểm thử đơn vị mà hy vọng bạn đã có. Bản gốc - https://simpleprogrammer.com/2017/01/30/tdd-unit-testing/
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION