Kiểm tra đơn vị ở Django


12

Tôi thực sự vật lộn để viết các bài kiểm tra đơn vị hiệu quả cho một dự án Django lớn. Tôi có phạm vi kiểm tra khá tốt, nhưng tôi nhận ra rằng các bài kiểm tra tôi đã viết chắc chắn là bài kiểm tra tích hợp / chấp nhận, hoàn toàn không phải bài kiểm tra đơn vị và tôi có những phần quan trọng trong ứng dụng của mình không được kiểm tra hiệu quả. Tôi muốn sửa cái này càng sớm càng tốt.

Đây là vấn đề của tôi. Lược đồ của tôi có mối quan hệ sâu sắc và được định hướng theo thời gian, mang lại cho đối tượng mô hình của tôi khả năng ghép nội bộ cao và nhiều trạng thái. Nhiều phương thức mô hình của tôi truy vấn dựa trên các khoảng thời gian và tôi đã có nhiều auto_now_addtiến triển trong các trường được đánh dấu thời gian. Vì vậy, lấy một phương thức trông như thế này chẳng hạn:

def summary(self, startTime=None, endTime=None):
    # ... logic to assign a proper start and end time 
    # if none was provided, probably using datetime.now()

    objects = self.related_model_set.manager_method.filter(...)

    return sum(object.key_method(startTime, endTime) for object in objects)

Làm thế nào để một cách tiếp cận kiểm tra một cái gì đó như thế này?

Đây là nơi tôi đang ở cho đến nay. Nó xảy ra với tôi rằng mục tiêu thử nghiệm đơn vị nên được đưa ra một số hành vi bị chế giễu by key_methodtrên các đối số của nó, là summarylọc / tổng hợp chính xác để tạo ra một kết quả chính xác?

Mocking datetime.now () là đủ đơn giản, nhưng làm thế nào tôi có thể giả định phần còn lại của hành vi?

  • Tôi có thể sử dụng đồ đạc, nhưng tôi đã nghe thấy những ưu và nhược điểm của việc sử dụng đồ đạc để xây dựng dữ liệu của mình (khả năng bảo trì kém là một kẻ lừa đảo đối với tôi).
  • Tôi cũng có thể thiết lập dữ liệu của mình thông qua ORM, nhưng điều đó có thể hạn chế, vì sau đó tôi cũng phải tạo các đối tượng liên quan. Và ORM không cho phép bạn lộn xộn với auto_now_addcác trường theo cách thủ công.
  • Mocking ORM là một tùy chọn khác, nhưng không chỉ khó để giả lập các phương thức ORM lồng nhau sâu sắc, mà logic trong mã ORM bị loại ra khỏi thử nghiệm, và việc nhạo báng dường như làm cho thử nghiệm thực sự phụ thuộc vào bên trong và phụ thuộc của chức năng dưới kiểm tra.

Các loại hạt cứng nhất để bẻ khóa dường như là các chức năng như thế này, nằm trên một vài lớp mô hình và các hàm cấp thấp hơn và phụ thuộc rất nhiều vào thời gian, mặc dù các chức năng này có thể không quá phức tạp. Vấn đề chung của tôi là cho dù tôi có thể cắt nó như thế nào, các bài kiểm tra của tôi trông phức tạp hơn các chức năng mà chúng đang kiểm tra.


Bạn nên viết thử nghiệm đơn vị trước tiên từ bây giờ, điều này giúp bạn phát hiện ra các vấn đề về khả năng kiểm tra trong thiết kế của bạn trước khi mã sản xuất thực tế được viết.
Chedy2149

2
Điều đó hữu ích, nhưng không thực sự giải quyết được vấn đề làm thế nào để kiểm tra tốt nhất các ứng dụng nặng ORM vốn có.
acjay

Bạn phải trừu tượng hóa lớp kiên trì
Chedy2149

1
Nghe có vẻ tuyệt vời theo giả thuyết, nhưng khi nói đến bảo trì dự án, tôi nghĩ rằng có một chi phí không hề nhỏ để chèn một lớp kiên trì tùy chỉnh được thực hiện giữa logic kinh doanh và Django ORM được ghi chép lại rất tốt. Đột nhiên, các lớp trở nên chật cứng với một loạt các phương thức trung gian nhỏ mà bản thân chúng cần được tái cấu trúc theo thời gian. Nhưng có lẽ điều này là hợp lý ở những nơi mà khả năng kiểm tra là rất quan trọng.
acjay

Kiểm tra cái này: vimeo.com/43612849 và cái này: vimeo.com/15007792
Chedy2149

Câu trả lời:


6

Tôi sẽ tiếp tục và đăng ký một câu trả lời cho những gì tôi đã đưa ra cho đến nay.

Giả thuyết của tôi là đối với một chức năng có khớp nối và trạng thái sâu, thực tế là nó chỉ đơn giản là sẽ mất rất nhiều dòng để kiểm soát bối cảnh bên ngoài của nó.

Đây là trường hợp thử nghiệm của tôi trông đại khái, dựa trên thư viện Mock tiêu chuẩn:

  1. Tôi sử dụng ORM tiêu chuẩn để thiết lập chuỗi sự kiện
  2. Tôi tạo sự khởi đầu của riêng mình datetimevà lật đổ auto_now_addthời gian để phù hợp với dòng thời gian cố định trong thiết kế của tôi. Tôi nghĩ rằng ORM đã không cho phép điều này, nhưng nó hoạt động tốt.
  3. Tôi đảm bảo rằng chức năng kiểm tra dưới chức năng sử dụng from datetime import datetimeđể tôi có thể vá datetime.now()chỉ trong chức năng đó (nếu tôi giả định toàn bộ datetimelớp, ORM sẽ phù hợp).
  4. Tôi tạo ra sự thay thế của riêng mình object.key_method(), với chức năng đơn giản nhưng được xác định rõ ràng phụ thuộc vào các đối số. Tôi muốn nó phụ thuộc vào các đối số, bởi vì nếu không tôi có thể không biết liệu logic của kiểm tra chức năng có hoạt động hay không. Trong trường hợp của tôi, nó chỉ đơn giản trả về số giây giữa startTimeendTime. Tôi vá nó bằng cách gói nó trong lambda và vá trực tiếp vào việc object.key_method()sử dụng new_callablekwarg của patch.
  5. Cuối cùng, tôi chạy một loạt các xác nhận về các cuộc gọi khác nhau summaryvới các đối số khác nhau để kiểm tra sự bằng nhau với các kết quả được tính toán bằng tay dự kiến ​​cho hành vi đã cho của giảkey_method

Không cần phải nói, điều này dài hơn và phức tạp hơn đáng kể so với chính chức năng. Nó phụ thuộc vào DB và không thực sự giống như một bài kiểm tra đơn vị. Nhưng nó cũng được tách ra khá nhiều từ bên trong của hàm - chỉ chữ ký và phụ thuộc của nó. Vì vậy, tôi nghĩ rằng nó thực sự có thể là một bài kiểm tra đơn vị.

Trong ứng dụng của tôi, chức năng này khá quan trọng và có thể tái cấu trúc để tối ưu hóa hiệu suất của nó. Vì vậy, tôi nghĩ rằng rắc rối là giá trị nó, mặc dù phức tạp. Nhưng tôi vẫn mở cho những ý tưởng tốt hơn về cách tiếp cận này. Tất cả một phần của hành trình dài của tôi hướng tới một phong cách phát triển dựa trên thử nghiệm nhiều hơn ...

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.