2010-11-03 29 views
10

Tôi vừa bắt đầu tìm hiểu về TDD và tôi đang phát triển một chương trình bằng GUI Tkinter. Vấn đề duy nhất là khi phương pháp .mainloop() được gọi, bộ kiểm tra sẽ treo cho đến khi cửa sổ đóng lại.Làm cách nào để chạy không nhất thiết trên ứng dụng Tkinter?

Dưới đây là một ví dụ về mã của tôi:

# server.py 
import Tkinter as tk 

class Server(tk.Tk): 
    def __init__(self): 
     tk.Tk.__init__(self) 
     self.mainloop() 

# test.py 
import unittest 
import server 

class ServerTestCase(unittest.TestCase): 
    def testClassSetup(self): 
     server.Server() 
     # and of course I can't call any server.whatever functions here 

if __name__ == '__main__': 
    unittest.main() 

Vì vậy, cách thích hợp các ứng dụng thử nghiệm Tkinter là gì? Hay chỉ là 'không'?

Cảm ơn!

Trả lời

1

Một điều bạn có thể làm là tạo ra vòng tròn chính trong một chuỗi riêng biệt và sử dụng chuỗi chính của bạn để chạy các thử nghiệm thực tế; xem chủ đề chính của nó. Hãy chắc chắn rằng bạn kiểm tra trạng thái của cửa sổ Tk trước khi thực hiện các xác nhận của bạn.

Đa luồng mọi mã đều khó. Bạn có thể muốn phá vỡ chương trình Tk của bạn thành các phần có thể kiểm tra thay vì kiểm tra đơn vị toàn bộ điều cùng một lúc (mà thực sự không phải là thử nghiệm đơn vị).

Tôi cuối cùng cũng đề xuất thử nghiệm ít nhất ở cấp độ kiểm soát nếu không thấp hơn cho chương trình của bạn, nó sẽ giúp bạn rất nhiều.

+0

Sau khi suy nghĩ về nó trong vài ngày (và làm việc trên các dự án khác), điều này có ý nghĩa hơn nhiều - có một lớp xử lý mà giao diện GUI sẽ làm cho việc kiểm tra dễ dàng hơn và có thể dễ dàng hơn để viết. –

+0

Vòng lặp sự kiện Tk cần chạy trong chuỗi chính. Vì vậy, sử dụng các chủ đề sinh ra cho các bài kiểm tra. – ankostis

+0

Không phải là chủ đề Tk-không an toàn? –

2

Có một kỹ thuật được gọi là vá khỉ, nhờ đó bạn thay đổi mã khi chạy.

Bạn có thể khỉ vá lớp TK, do đó vòng lặp chính không thực sự bắt đầu chương trình.

Something như thế này trong test.py của bạn (chưa được kiểm tra!):

import tk 
class FakeTk(object): 
    def mainloop(self): 
     pass 

tk.__dict__['Tk'] = FakeTk 
import server 

def test_server(): 
    s = server.Server() 
    server.mainloop() # shouldn't endless loop on you now... 

Một khuôn khổ mocking như mock làm cho điều này ít hơn rất nhiều đau đớn.

+1

Đây là tuyến đường tốt nhất. Cuối cùng tôi đã nghi ngờ rằng Tk không đóng cửa sạch sẽ giữa những người không nhận tiền. (Tôi sử dụng mô-đun unittest từ thư viện chuẩn.) Nó sẽ phức tạp hơn một chút nếu lớp là một phức tạp của các vật dụng tkinter. Trong TDD bạn phải khởi tạo đối tượng để thử nghiệm trong khi khỉ vá bất kỳ nỗ lực nào để bắt đầu Tk. Điều này thậm chí ảnh hưởng đến việc thực hiện '__str__' và' __repr__'. – lemi57ssss

+0

Nếu không có Tk logic tham gia, bạn không thể chắc chắn rằng hành động của bạn có ảnh hưởng mong muốn trên giao diện người dùng. –

+0

Đồng ý, nhưng đó không phải là thử nghiệm đơn vị. Tôi muốn thử nghiệm theo một cách khác, có lẽ với một kịch bản tự động hóa giao diện người dùng. –

2

Câu trả lời của @ Gin Ginom đề cập đến mô phỏng. Công thức Hoạt động của Tiểu bang này cho thấy cách thức giả tránh vấn đề của bộ kiểm tra treo của OP: Robust Unittesting of Tkinter Menu Items with Mocking. Bộ thử nghiệm trong công thức này không bao gồm bất kỳ lệnh gọi nào tới số mainloop.

+0

Tác giả của bài đăng tự thừa nhận rằng _ "Cách tiếp cận cuối cùng thất bại vì các tác dụng phụ không thể bỏ qua mà không thể xóa trong phương thức tearDown." _ –

+0

Bên cạnh đó, dường như anh ta không chạy vòng lặp sự kiện, do đó trình xử lý sự kiện sẽ không kích hoạt. Trong mã ["mã hiện tại của tôi"] (https://github.com/trin5tensa/pigjar/blob/master/pigjar/test/test_guidialogs.py) được liên kết ở đó, anh ta chỉ kiểm tra trạng thái ban đầu. –

+0

Trong khi liên kết này có thể trả lời câu hỏi, tốt hơn nên bao gồm các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo. Câu trả lời chỉ liên kết có thể trở thành không hợp lệ nếu trang được liên kết thay đổi. - [Từ đánh giá] (/ đánh giá/bài đăng chất lượng thấp/18960614) –

1

Dòng dưới cùng: bơm các sự kiện có mã bên dưới sau một hành động gây ra sự kiện giao diện người dùng, trước khi hành động sau đó cần tác động của sự kiện đó.


IPython cung cấp giải pháp thanh lịch mà không đề nó gui tk lệnh ma thuật thực hiện của nó mà nằm ở terminal/pt_inputhooks/tk.py.

Thay vì root.mainloop(), nó chạy root.dooneevent() là vòng lặp, kiểm tra điều kiện thoát (lần nhập đầu vào tương tác) mỗi lần lặp lại. Bằng cách này, vòng lặp thậm chí không chạy khi IPython bận xử lý một lệnh.

Với thử nghiệm, không có sự kiện bên ngoài nào để chờ và thử nghiệm luôn "bận", vì vậy, thử nghiệm phải chạy theo cách thủ công (hoặc bán tự động) tại "thời điểm thích hợp". Họ là ai?

Thử nghiệm cho thấy rằng không có vòng lặp sự kiện, người ta có thể thay đổi trực tiếp tiện ích (với <widget>.tk.call() và bất kỳ thứ gì kết thúc tốt đẹp), nhưng trình xử lý sự kiện không bao giờ kích hoạt. Vì vậy, vòng lặp cần phải được chạy bất cứ khi nào một sự kiện xảy ra và chúng tôi cần hiệu ứng của nó - tức là sau bất kỳ thao tác nào thay đổi điều gì đó.

Mã này, xuất phát từ thủ tục IPython nói trên, sẽ là:

def pump_events(root): 
    while root.dooneevent(_tkinter.ALL_EVENTS|_tkinter.DONT_WAIT): 
     pass 

Điều đó sẽ xử lý (thực hiện xử lý cho) tất cả các sự kiện chờ giải quyết, và tất cả các sự kiện mà sẽ cho kết quả trực tiếp từ những người.

(tkinter.Tk.dooneevent() đại biểu đến Tcl_DoOneEvent().)

Các vấn đề liên quan