2008-10-07 39 views
12

Tôi đang trong một dự án mà chúng tôi đang bắt đầu tái cấu trúc một số cơ sở mã lớn. Một vấn đề nảy sinh ngay lập tức là mỗi tệp nhập nhiều tệp khác. Làm thế nào để tôi một cách thanh lịch giả lập điều này trong bài kiểm tra đơn vị của tôi mà không cần phải thay đổi mã thực tế để tôi có thể bắt đầu viết đơn vị kiểm tra?Python, kiểm tra đơn vị và nhập khẩu mocking

Ví dụ: Tệp có chức năng tôi muốn kiểm tra, nhập 10 tệp khác là một phần của phần mềm của chúng tôi chứ không phải là libs lõi python.

Tôi muốn có thể chạy các bài kiểm tra đơn vị một cách riêng biệt nhất có thể và bây giờ tôi sẽ chỉ kiểm tra các hàm không phụ thuộc vào những thứ từ các tệp đang được nhập.

Cảm ơn tất cả các câu trả lời.

Tôi thực sự không biết mình muốn làm gì từ đầu nhưng giờ tôi nghĩ mình biết.

Vấn đề là một số lần nhập chỉ có thể thực hiện được khi toàn bộ ứng dụng đang chạy vì một số phép thuật tự động của bên thứ ba. Vì vậy, tôi đã phải thực hiện một số sơ khai cho các mô-đun này trong một thư mục mà tôi đã chỉ ra với sys.path

Bây giờ tôi có thể nhập tệp chứa các hàm tôi muốn viết thử nghiệm trong tệp thử nghiệm đơn vị của mình mà không có khiếu nại về thiếu mô-đun.

Trả lời

7

Nếu bạn muốn nhập mô-đun trong khi đồng thời đảm bảo rằng nó không nhập bất kỳ thứ gì, bạn có thể thay thế hàm dựng sẵn __import__.

Ví dụ, sử dụng lớp này:

class ImportWrapper(object): 
    def __init__(self, real_import): 
     self.real_import = real_import 

    def wrapper(self, wantedModules): 
     def inner(moduleName, *args, **kwargs): 
      if moduleName in wantedModules: 
       print "IMPORTING MODULE", moduleName 
       self.real_import(*args, **kwargs) 
      else: 
       print "NOT IMPORTING MODULE", moduleName 
     return inner 

    def mock_import(self, moduleName, wantedModules): 
     __builtins__.__import__ = self.wrapper(wantedModules) 
     try: 
      __import__(moduleName, globals(), locals(), [], -1) 
     finally: 
      __builtins__.__import__ = self.real_import 

Và trong mã thử nghiệm của bạn, thay vì viết import myModule, viết:

wrapper = ImportWrapper(__import__) 
wrapper.mock_import('myModule', []) 

Đối số thứ hai để mock_import là một danh sách các tên mô-đun bạn do muốn nhập trong mô-đun bên trong.

Ví dụ này có thể được sửa đổi thêm để ví dụ:nhập khẩu các mô-đun khác hơn mong muốn thay vì chỉ không nhập khẩu nó, hoặc thậm chí mocking các đối tượng mô-đun với một số đối tượng tùy chỉnh của riêng bạn.

+0

Bạn muốn thêm 'try: finally:' trên phương thức mock_import, để tránh rời khỏi hệ thống với gói được nhập thay vì nhập mặc định trong trường hợp lỗi – Yonatan

+0

@Yonatan: bạn nói đúng, cảm ơn! Tôi đã sửa đổi mã của mình. – DzinX

1

"nhập nhiều tệp khác"? Nhập nhiều tệp khác là một phần của cơ sở mã tùy chỉnh của bạn? Hoặc nhập nhiều tệp khác là một phần của phân phối Python? Hoặc nhập nhiều tệp dự án nguồn mở khác?

Nếu hàng nhập của bạn không hoạt động, bạn gặp sự cố "đơn giản" PYTHONPAT H. Nhận tất cả các thư mục dự án khác nhau của bạn vào một số PYTHONPATH mà bạn có thể sử dụng để thử nghiệm. Chúng tôi có một con đường khá phức tạp, trong Windows chúng tôi quản lý nó như thế này

@set Part1=c:\blah\blah\blah 
@set Part2=c:\some\other\path 
@set that=g:\shared\stuff 
set PYTHONPATH=%part1%;%part2%;%that% 

Chúng tôi giữ cho mỗi mảnh của con đường riêng biệt để chúng ta (a) biết nơi mà mọi thứ đến từ và (b) có thể quản lý sự thay đổi khi chúng ta di chuyển những thứ xung quanh.

Vì số PYTHONPATH được tìm kiếm theo thứ tự, chúng tôi có thể kiểm soát những gì được sử dụng bằng cách điều chỉnh thứ tự trên đường dẫn.

Khi bạn có "mọi thứ", nó sẽ trở thành một câu hỏi tin cậy.

Hoặc

  • bạn tin tưởng một cái gì đó (ví dụ: cơ sở mã Python) và chỉ cần import nó.

Hoặc

  • Bạn không tin tưởng một cái gì đó (ví dụ: mã của riêng bạn) và bạn

    1. thử nghiệm nó một cách riêng biệt và
    2. nhạo báng nó để thử nghiệm độc lập.

Bạn có kiểm tra thư viện Python không? Nếu vậy, bạn đã có rất nhiều công việc.Nếu không, sau đó, bạn nên có lẽ chỉ mock ra những điều bạn thực sự sẽ kiểm tra.

1

Nếu bạn thực sự muốn muck xung quanh với cơ chế nhập khẩu python, hãy xem mô-đun ihooks. Nó cung cấp các công cụ để thay đổi hành vi của tích hợp __import__. Nhưng nó không rõ ràng từ câu hỏi của bạn tại sao bạn cần phải làm điều này.

0

Không cần thao tác khó khăn nếu bạn muốn sửa chữa nhanh chóng và bẩn trước khi kiểm tra đơn vị của bạn.

Nếu kiểm tra đơn vị nằm trong cùng một tệp với mã bạn muốn kiểm tra, chỉ cần xóa mô-đun không mong muốn khỏi từ điển globals().

Dưới đây là một ví dụ khá dài: giả sử bạn có một mô-đun impp.py với nội dung:

value = 5 

Bây giờ, trong tập tin thử nghiệm của bạn, bạn có thể viết:

>>> import impp 
>>> print globals().keys() 
>>> def printVal(): 
>>>  print impp.value 
['printVal', '__builtins__', '__file__', 'impp', '__name__', '__doc__'] 

Lưu ý rằng impp là một trong những globals, bởi vì nó đã được nhập khẩu. Gọi hàm printVal có sử dụng mô-đun impp vẫn hoạt động:

>>> printVal() 
5 

Nhưng bây giờ, nếu bạn loại bỏ impp chính từ globals() ...

>>> del globals()['impp'] 
>>> print globals().keys() 
['printVal', '__builtins__', '__file__', '__name__', '__doc__'] 

... và cố gắng gọi printVal(), bạn sẽ get:

>>> printVal() 
Traceback (most recent call last): 
    File "test_imp.py", line 13, in <module> 
    printVal() 
    File "test_imp.py", line 5, in printVal 
    print impp.value 
NameError: global name 'impp' is not defined 

... có lẽ chính xác những gì bạn đang cố gắng đạt được.

Để sử dụng nó trong các bài kiểm tra đơn vị, bạn có thể xóa các hình cầu ngay trước khi chạy bộ kiểm tra, ví dụ: trong __main__:

if __name__ == '__main__': 
    del globals()['impp'] 
    unittest.main() 
+0

Cảm ơn câu trả lời của bạn, tôi đã học được rất nhiều từ nó. Nhưng nó thực sự là đối diện tôi muốn làm. Các bài kiểm tra nằm trong một tệp khác và khi tôi nhập tệp này chứa các hàm tôi muốn kiểm tra, tôi muốn tệp đó nghĩ rằng tất cả các tệp khác đã được nhập. –

0

Trong bình luận của bạn above, bạn nói bạn muốn thuyết phục python rằng một số các module đã được nhập. Điều này vẫn có vẻ giống như một mục tiêu kỳ lạ, nhưng nếu đó thực sự là những gì bạn muốn làm, về nguyên tắc bạn có thể lẻn vào phía sau lưng của cơ chế nhập khẩu, và thay đổi sys.modules. Bạn không chắc chắn làm thế nào điều này sẽ làm việc cho nhập khẩu gói, nhưng nên được tốt cho nhập khẩu tuyệt đối.