1

I am attempting to implement unittests (while learning about them) for a Tkinter application that I wrote. For that purpose I have managed to create a very minimalistic unittest around the basic structure of my application as shown below (passing it a Tkinter instance as master):

classTest.py

#! /usr/bin/env python
from Tkinter import *

class App():
    def __init__(self,master):
        self.text = "foo"
        self.printy()

    def printy(self):
        print self.text
        return "test"

# Call the main app
if __name__ == "__main__":
    root = Tk()
    app = App(root)
    root.mainloop()

test.py

#! /usr/bin/env python
from Tkinter import *
import unittest
import classTest

class testTest(unittest.TestCase):
    def test(self):
        a = classTest.App(Tk())
        self.assertEqual(a.printy(),"test")

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

This test returns that it ran succesful, although it does print foo twice. However, when I then attempt to run the same concept on my whole application it crashes on the __init__ of my class.

unitTest.py

#! /usr/bin/env python
import unittest
from Tkinter import *
import MassyTools

class readTest(unittest.TestCase):
    def test(self):
        a = MassyTools.App(Tk())
        self.assertEqual(a.readData("./tests/calibrated_IBD cohort sample H6-run 1_0_E24_1.xy")[0][0],1299.11)

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

Running this test crashes it on the fact that root is not defined (see below error).

enter image description here

I have been fiddling around with mocking as per this blogpost but I don't really grasp the concept yet. Below is a subset of the MassyTools.py file including the offending self.toolbar line:

class App():

    def __init__(self, master):
        # The Canvas
        self.canvas = FigureCanvasTkAgg(self.fig, master=master)
        self.toolbar = NavigationToolbar2TkAgg(self.canvas, root)
        self.canvas.get_tk_widget().pack(fill=BOTH, expand=YES)
        self.canvas.draw()

Therefore, the question is if I should instantiate it differently in the unittest, modify the application to have a fake root if called in this way or something else entirely.

Bas Jansen
  • 3,273
  • 5
  • 30
  • 66

1 Answers1

1

I found the issue in the application that I wanted to test, I declared root = Tk() in the main. This root was passed to the App class as master but in the program I made a call to root where it should have been to master. The below changes removes the initial crash, I also included a call to atexit to not have to physically close the application (as per this answer).

Revised application:

class App():

    def __init__(self, master):

        # The Canvas
        self.canvas = FigureCanvasTkAgg(self.fig, master=master)
        self.toolbar = NavigationToolbar2TkAgg(self.canvas, master)
        self.canvas.get_tk_widget().pack(fill=BOTH, expand=YES)
        self.canvas.draw()

# Call the main app
if __name__ == "__main__":
    root = Tk()
    app = App(root)
    root.mainloop()

I adjusted my unittest.py code as follows, which now finishes with an OK after i physically close the application:

#! /usr/bin/env python
import unittest
from Tkinter import *
import MassyTools
import atexit

class readTest(unittest.TestCase):
    def test(self):
        self.assertEqual(a.readData("./tests/calibrated_IBD cohort sample H6-run 1_0_E24_1.xy")[0][0],1299.11)

if __name__ == "__main__":
    # Set up Tk(), instantiate the application and close it right away.
    root = Tk()
    a = MassyTools.App(root)
    atexit.register(root.mainloop)
    root.destroy()

    # Start the actual tests
    unittest.main(verbosity=2)
Community
  • 1
  • 1
Bas Jansen
  • 3,273
  • 5
  • 30
  • 66
  • For future wanderers: readTest class should also contain the creation of an instance (a = MassyTools.App(Tk()). Otherwise, readData() is called on an unknown object(a). – Jakub Bartolomej Kosuth Jan 24 '20 at 10:59