HomeOur Team
Java Unit Testing - Phần 1 - Unit Testing trong phát triển phần mềm
Java Unit Testing - Phần 1 - Unit Testing trong phát triển phần mềm
hai.nguyen
hai.nguyen
November 26, 2020
5 min

1. Unit Testing (kiểm thử đơn vị)

1.1 Định nghĩa

Unit Test (UT) là kỹ thuật kiểm thử những khối thành phần nhỏ nhất trong phần mềm (thường là các hàm hoặc phương thức). Đây là một trong những cấp độ kiểm thử đơn giản và có thể bắt đầu sớm trong vòng đời phát triển phần mềm. Thậm chí, chúng ta có thể viết unit test trước khi viết mã. Tuy nhiên, đây không phải là một thuật ngữ mới trong lĩnh vực phần mềm. Thông thường, “unit” sẽ được quy định giới hạn trong một hàm (method) hay một class. Trong thực tế, tùy vào kinh nghiệm và kĩ năng, developer sẽ đưa ra quyết định viết các UT như thế nào cho phù hợp, có thể test đầu vào đầu ra của hàm, hay kiểm tra một phần hoặc toàn bộ class.

Unit test là test do developer viết, được chạy để kiểm tra các hàm do developer viết ra có sai hay không. UT thường được chạy mỗi khi build để đảm bảo các hàm đều chạy đúng sau khi ta sửa code.

UT là các đoạn code có cấu trúc giống như các đối tượng được xây dựng để kiểm tra từng bộ phận trong hệ thống. Mỗi UT sẽ gửi một số yêu cầu đầu vào và kiểm tra kết quả đầu ra có đúng hay không, bao gồm:

  • Các kết quả trả về mong muốn
  • Các lỗi ngoại lệ mong muốn

Các đoạn mã UT hoạt động liên tục hoặc định kỳ để thăm dò và phát hiện các lỗi kỹ thuật trong suốt quá trình phát triển, do đó UT còn được gọi là kỹ thuật kiểm nghiệm tự động.

1.2 Vòng đời của UT

UT có 3 trạng thái cơ bản:

  • Fail (trạng thái lỗi)
  • Ignore (tạm ngừng thực hiện)
  • Pass (trạng thái làm việc)

Toàn bộ UT được vận hành trong một hệ thống tách biệt. Có rất nhiều phần mềm hỗ trợ thực thi UT với giao diện trực quan. Thông thường, trạng thái của UT được biểu hiện bằng các màu khác nhau: màu xanh (pass), màu vàng (ignore) và màu đỏ (fail).

UT chỉ thực sự đem lại hiệu quả khi:

  • Được vận hành lặp lại nhiều lần.
  • Tự động hoàn toàn.
  • Độc lập với các UT khác.

1.3 Thiết kế UT

Mỗi UT đều được tiết kế theo trình tự sau:

  • Given: Thiết lập các điều kiện cần thiết: khởi tạo các đối tượng, xác định tài nguyên cần thiết, xây dựng các dữ liệu giả, …
  • When: Gọi các phương thức cần kiểm tra
  • Then: Kiểm tra sự hoạt động đúng đắn của các phương thức theo mong muốn
  • Dọn dẹp tài nguyên sau khi kết thúc kiểm tra (nếu có)

2. Xây dựng UT với mô hình đối tượng ảo (Mock Object)

Trong UT, mỗi một đối tượng hay một phương thức riêng lẻ được kiểm tra tại một thời điểm và chúng ta chỉ quan tâm đến các trách nhiệm của chúng có được thực hiện đúng hay không. Tuy nhiên trong các dự án phần mềm phức tạp thì UT không còn là quy trình riêng lẻ, nhiều đối tượng (đơn vị chương trình) không làm việc độc lập mà tương tác với các đối tượng khác như kết nối mạng, cơ sở dữ liệu hay dịch vụ web. Như vậy công việc kiểm nghiệm có thể bị trì hoãn gây tác động xấu đến quy trình phát triển chung. Để giải quyết các vấn đề này người ta đưa ra mô hình “Mock Object” hay đối tượng ảo (hoặc đối tượng giả).

2.1 Định nghĩa

Mock object (MO) là một đối tượng ảo, mô phỏng các tính chất và hành vi giống hệt như đối tượng thực được truyền vào bên trong khối mã đang vận hành nhằm kiểm tra tính đúng đắn của các hoạt động bên trong.

Thay vì lấy data từ một real service, chúng ta sử dụng một bộ test data mà input và output đã được định nghĩa rõ ràng từ một Mock Object và chúng ta có thể dùng nó cho đối tượng muốn test.

2.2 Đặc điểm

  • Đơn giản hơn đối tượng thực nhưng vẫn giữ được sự tương tác với các đối tượng khác
  • Không lặp lại nội dung đối tượng thực
  • Cho phép thiết lập các trạng thái riêng trợ giúp kiểm tra

2.3 Lợi ích

  • Đảm bảo công việc kiểm nghiệm không bị gián đoạn bởi các yếu tố bên ngoài, giúp các người viết tập trung vào một chức năng nghiệp vụ cụ thể, từ đó tạo ra UT vận hành nhanh hơn
  • Giúp tiếp cận hướng đối tượng tốt hơn. Nhờ MO chúng ta có thể phát hiện interface cần tách ở một số lớp
  • Dễ dàng cho việc kiểm nghiệm. Thay vì gọi các đối tượng thực vận hành nặng nề, chúng ta có thể gọi các MO đơn giản hơn để kiểm tra nhanh liên kết giữa các thủ tục, công việc kiểm nghiệm có thể tiến hành nhanh hơn

2.4 Phạm vi sử dụng

MO được sử dụng trong các trường hợp sau:

  • Khi cần lập trạng thái giả của một đối tượng thực trước khi các UT có liên quan được đưa vào vận hành (ví dụ kết nối cơ sở dữ liệu, giả định trạng thái lỗi server, …)
  • Khi cần lập trạng thái cần thiết cho một số tính chất nào đó của đối tượng đã bị khoá quyền truy cập (các biến, thủ tục, hàm, thuộc tính riêng được khai báo private). Không phải lúc nào các tính chất của một đối tượng cũng có thể được mở rộng phạm vi truy cập ra bên ngoài vì điều này có thể trực tiếp phá vỡ liên kết giữa các phương thức theo một trình tự sắp đặt trước, từ đó dẫn đến kết quả có thể bị xử lý sai. Tuy nhiên, MO có thể thiết lập các trạng thái giả mà vẫn đảm bảo các yêu cầu ràng buộc, các nguyên tắc đúng đắn và các quan hệ của đối tượng thực
  • Khi cần kiểm tra một số thủ tục hoặc các biến của thành viên bị hạn chế truy cập. Bằng cách kế thừa MO từ đối tượng thực chúng ta có thể kiểm tra các thành viên đã được bảo vệ (khai báo protected)
  • Khi cần loại bỏ các hiệu ứng phụ của một đối tượng nào đó không liên quan đến UT
  • Khi cần kiểm nghiệm mã vận hành có tương tác với hệ thống bên ngoài

2.5 Thiết kế Mock Object (MO)

Thông thường, nếu số lượng MO không nhiều, chúng ta có thể tự thiết kế. Nếu không muốn mất nhiều thời gian tự thiết kế một số lượng lớn MO, bạn có thể tải về các công cụ có sẵn thông dụng hiện nay như Mockito, PowerMockito, … Các phần mềm này cung cấp nhiều API cho phép xây dựng MO và các kho dữ liệu giả dễ dàng hơn, cũng như kiểm tra tự động các số liệu trong UT. Nói chung, việc thiết kế MO gồm 3 bước chính sau đây:

  • Đưa ra interface để mô tả đối tượng. Tất cả các tính chất và thủ tục quan trọng cần kiểm tra phải được mô tả trong interface
  • Viết nội dung cho đối tượng thực dựa trên interface như thông thường
  • Trích interface từ đối tượng thực và triển khai MO dựa trên interface đó

Lưu ý: MO phải được đưa vào quy trình kiểm nghiệm tách biệt. Cách này có thể sinh ra nhiều interface không thực sự cần thiết, có thể làm cho thiết kế ứng dụng trở nên phức tạp. Một cách làm khác là kế thừa một đối tượng đang tồn tại và cố gắng mô phỏng các hành vi càng đơn giản càng tốt, như trả về một dữ liệu giả chẳng hạn. Đặc biệt tránh tạo ra những liên kết mắt xích giữa các MO vì chúng có thể làm cho thiết kế UT trở nên phức tạp.

Tham khảo GP Coder


Tags

#java#unittest#112020
hai.nguyen

hai.nguyen

Developer

Expertise

java
© 2021, All Rights Reserved.

Quick Links

HomeOur Team

Social Media