Surcouf
Surcouf

Reputation: 91

Testing tkinter application

I wrote a small application using python 3 and tkinter. Testing every widget, even though there are not many of them feels daunting so I wanted to write a couple of automated tests to simplify the process. I read some other question that seemed relevant to this problem but none fit my needs. Right now I'm doing the testing in a very simple manner - I invoke the command for every widget and manually click through it to see if it works. It does make things a bit faster, but I constantly run into some problems - i.e. I can't automatically close popup windows (like showinfo) even with using libraries to simulate keyboard clicks (namely pynput). Is there an efficient approach for testing applications using tkinter?

Here is the code I use right now:

import tkinter as tkinter
import unittest
from mygui import MyGUI

class TKinterTestCase(unittest.TestCase):
    def setUp(self):
        self.root = tkinter.Tk()


    def tearDown(self):
        if self.root:
            self.root.destroy()

    def test_enter(self):
        v = MyGUI(self.root)
        v.info_button.invoke()
        v.close_button.invoke()
        v.btnOut.invoke()


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

Upvotes: 2

Views: 5716

Answers (1)

j_4321
j_4321

Reputation: 16169

I don't know much about unittest but I found a workaround to close popup dialogs like showinfo during the tests. The idea is to use keyboard event to invoke the button of the dialog. But since the app is waiting for the user to close the popup dialog, we need to schedule in advance the keyboard event using after:

self.root.after(100, self.root.event_generate('<Return>'))
v.button.invoke()

Full example

import tkinter
from tkinter import messagebox
import unittest


class MyGUI(tkinter.Frame):
    def __init__(self, master, **kw):
        tkinter.Frame.__init__(self, master, **kw)
        self.info_button = tkinter.Button(self, command=self.info_cmd, text='Info')
        self.info_button.pack()
        self.quit_button = tkinter.Button(self, command=self.quit_cmd, text='Quit')
        self.quit_button.pack()

    def info_cmd(self):
        messagebox.showinfo('Info', master=self)

    def quit_cmd(self):
        confirm = messagebox.askokcancel('Quit?', master=self)
        if confirm:
            self.destroy()


class TKinterTestCase(unittest.TestCase):
    def setUp(self):
        self.root = tkinter.Tk()
        self.root.bind('<Key>', lambda e: print(self.root, e.keysym))

    def tearDown(self):
        if self.root:
            self.root.destroy()

    def test_enter(self):
        v = MyGUI(self.root)
        v.pack()
        self.root.update_idletasks()

        # info
        v.after(100, lambda: self.root.event_generate('<Return>'))
        v.info_button.invoke()

        # quit
        def cancel():
            self.root.event_generate('<Tab>')
            self.root.event_generate('<Return>')

        v.after(100, cancel)
        v.quit_button.invoke()
        self.assertTrue(v.winfo_ismapped())    
        v.after(100, lambda: self.root.event_generate('<Return>'))
        v.quit_button.invoke()
        with self.assertRaises(tkinter.TclError):
            v.winfo_ismapped()


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

Upvotes: 5

Related Questions