Mock Objects: Khuyết điểm và hữu dụng(p1)
Tóm tắt
Viết một Unit tests thật ko dễ dàng. Phần lớn thời gian ta dùng là test code kết nối database, với server, hoặc với những modules khác(chưa được viết xong). Với một số lượng lớn điều kiện như vậy thì ko dể gì hiện thực một môi trường test. Việc cài đặt những điều kiện test làm ta mất đi một số lượng lớn thời gian và tiền bạc, và có lẽ những lợi thế của test ko còn như ta mong muốn. Bài viết này dùng Mock Objects, một kỹ thuật test từ cộng đồng XP, khuyến khích cô lập code bằng cách tạo những ràng buộc giả. Như những tool khác, ta cần phải cẩn trọng khi dùng, và ko nên lạm dụng.
Mock Objects Overview
Những năm gần đây, những developer đã làm sống lại lợi thế của việc dùng test, đồng ý rằng việc tìm kiếm và gắn kết các thành phần là khó khăn. Và kết quả là Unit Testing trở thành một thành phần chính của software development, là một cách để tìm lỗi và đồng nhất yêu cầu. Thành phần chính của unit testing là test bằng cách cô lập các thành phần, và thường là class. Test cách ly là rất khó khăn, đặc biệt là khi nó giao tiếp với một phần khó, hoặc tạo nhanh một test. Khó khăn hơn là viết và maintain unit tests, và đó là lý do mà developers chán nản làm test.
Họ đã miêu tả những khó khăn và đưa ra hướng giải quyết:
Tạo những sự phụ thuộc phức tạp(ví dụ như một database)
Kiểm tra những actions cần thiết để run code(ví dụ như một JDBC conection đã đóng sau khi dùng )
Tạo môi trường là rất khó khăn (lỗi thể hiện cảu câu SQL)
Mặc dù hữu dụng, nhưng chắc chắn nó không thể làm được tất cả, việc lạm dụng chỉ làm cho chất lượng của project giảm xuống.
Nhược điểm của Mocks
Hãy nhìn vào những điều cần biết khi dùng.
Mocks ẩn chứa nhiều vấn đề
Figure 1. Lưu thông tin của một employee vào database
Class EmployeeBO cung cấp những business services cho Employees và dùng EmployeeDAO để lưu dữ liệu vào CSDL quan hệ dùng JDBC. Test EmployeeBO cần có bước cài đặt database và cung cấp nó cho việc test.
Đề nghị của mock objects là bạn có thể lưu bằng cách giả lặp một EmployeeDAO, và không cần tạo một database. Mocks giúp cho việc tạo và run nhanh hơn, nhưng chúng ko cho ta sự tin cậy vào hệ thống, như một cách làm phù hợp. Mock testing có thể có bugs hay sai sót trong các thành phần được giả định. Để tìm một sai xót, thì chúng ta cần phải có một bài test tổng thể. Trong ví dụ trên, hệ thống cần test dùng database để lưu thông tin employee. Mock testing hạn chế khi kiểm tra sự chính sác của tương tác giữa EmployeeBO và EmployeeDAO—vì vậy, EmployeeBO chỉ gọi một vài phương thức từ EmployeeDAO trong cùng thời gian. Chỉ có test tổng thể mới có thể giúp ta tìm vấn đề, nhưng lỗi trong JDBC driver hay trong chính bản thân database, cái này chỉ xuất hiện khi application trở thành sản phẩm.
Mocks add clutter and duplication to test code
Đoạn code sau dùng EasyMock để test EmployeeBO dùng EmployeeDAO để lưu thông tin một employees mới và update thông tin một employee đã tồn tại.
@Before public void setUp() {
mockEmployeeDAO = createMock(EmployeeDAO.class);
employeeBO = new EmployeeBO(mockEmployeeDAO);
employee = new Employee("Alex", "CA", "US");
}
@Test public void shouldAddNewEmployee() {
mockEmployeeDAO.insert(employee);
replay(mockEmployeeDAO);
employeeBO.addNewEmployee(employee);
verify(mockEmployeeDAO);
}
@Test public void shouldUpdateEmployee() {
mockEmployeeDAO.update(employee);
replay(mockEmployeeDAO);
employeeBO.updateEmployee(employee);
verify(mockEmployeeDAO);
}
Phương thức shouldAddNewEmployee kiểm tra sự ảnh hưởng giữa một đối tượng đại diện cho (employeeBO) và một đối tượng đại diện cho (mockEmployeeDAO). Mong chờ employeeBO gọi phương thức insert trong mockEmployeeDAO, qua một thể hiện của Employee nhận được. Qua một vì dụ đơn giản, phương thức shouldAddNewEmployee đã không như ta mong muốn, thêm lôn sộn cho bài test:
A gọi replay để báo cho EasyMock rằng tất cả yêu cầu đã thi hành
A gọi verify để báo cho EasyMock đã kiểm tra
Cách dùng mocks thường theo pattern:
1.Cài mock(s) và dự tính
2.Chạy test
3.Xác nhận dự tính đã thi hành
Như một đã nói duplication in test code, đã hiển thị rỏ trong phương thức shouldAddNewEmployee() và shouldUpdateEmployee(). Class sau , EasyMockTemplate, có thể giúp giảm bớt code clutter and duplication:
/**
* Understands a template for usage of EasyMock mocks.
* @author Alex Ruiz
*/
public abstract class EasyMockTemplate {
/** Mock objects managed by this template */
private final List
2 Responses to "Mock Objects: Khuyết điểm và hữu dụng(p1)"
Thực tế thì dùng mock dễ maintain vì nó isolate module cần test với các module khác, test hỏng khi và chỉ khi ta thay đổi ràng buộc giữa các module, chuyện này không phải lỗi của mock. Có hay không có mock cũng gặp thôi
>Trong ví dụ trên, EmployeeBO tác động với EmployeeDAO để lưu thông tin employee vào database dùng JDBC. Hãy thay đổi cách lưu xuống database – từ jdbc sang jpa chẳng hạn- bằng cách thay EmployeeDAO với EmployeeJPA, lưu cùng một thông tin xuống cùng một database. Chúng tôi mong rằng sẽ dùng được cái test được viết sẵn, và dĩ nhiên là kết quả ko thay đổi. Không may, test của chúng ta dùng morks sẽ ko đơn giản vì tương tác giữa EmployeeBO và EmployeeDAO ko tồn tại: EmployeeBO dùng EmployeeJPA để lưu xuống database.
Em nên phân biệt là unit test khác với integration hay functional testing. Cho nên case ở trên không phải là yếu điểm của mock object
>Bình thường mocks, một mocks đều hiện thực của một interface, có những phương thức dự tính, có thể cần cân nhắc nhiều hay ít ởn định. Trái lại, các pt trên intefaces có thể là phương thức protected hay package-protected, tương ứng với hiện thực của class. Như hiện thực có thể thay đổi vào một lúc nào đó, thay sự ảnh hưởng giữa code giữa các mocks, sự thay đổi tăng dần và sẽ phá bỏ logic test.
Interface là specification thì khác vói concrete class là implementation. Nếu vì implementation mà làm thay đổi test code thì nên xem lại mình đã design tốt chưa
Hai79
Đăng nhận xét