0

I am trying to build a simple unit test on a calculator Tkinter script. The first test case went well with title text, but I haven't figured out the way to click (trigger) the button according to the widget text. For example, execute three buttons 3 + 5 will output 8. Then I can assert the test case for addition. Thanks.

Here is my test script:

import unittest
from tkinter import *
from calculator2 import start_application

class TestCalculator2(unittest.TestCase):

  # start the application, test, and destroy at the end of the test
  async def _start_app(self):
    self.app.mainloop() 

  def setUp(self):
    self.app = start_application()
    self._start_app()

  def tearDown(self):
    self.app.destroy()
    
class TestCalculation(TestCalculator2):

  def test_startup(self):
    title = self.app.winfo_toplevel().title()
    expected = "Calculator"
    self.assertEqual(title, expected)

  def test_addition(self):
    # need to figure out a way to execute the button according to its text
    pass

The calculator script is large, so I put it into repl.it. Please advice if there's better place to share the code.

Edit 1 The calculator code:

# importing Tkinter and math
from tkinter import *
import math

# calc class


class calc:

  def getandreplace(self):
    """replace x with * and ÷ with /"""
    self.expression = self.e.get()
    self.newtext = self.expression.replace('/', '/')
    self.newtext = self.newtext.replace('x', '*')

  def equals(self):
    """when the equal button is pressed"""
    self.getandreplace()
    try:
      # evaluate the expression using the eval function
      self.value = eval(self.newtext)
    except SyntaxError or NameError:
      self.e.delete(0, END)
      self.e.insert(0, 'Invalid Input!')
    else:
      self.e.delete(0, END)
      self.e.insert(0, self.value)

  def clearall(self):
    """when clear button is pressed,clears the text input area"""
    self.e.delete(0, END)

  def action(self, argi):
    """pressed button's value is inserted into the end of the text area"""
    self.e.insert(END, argi)

  def callback(self, e):
    print(e.widget) # print out the button clicked https://stackoverflow.com/questions/4299145/getting-the-widget-that-triggered-an-event
    e.widget.invoke()
    return e.widget

  def __init__(self,master):
      """Constructor method"""
      master.title('Calculator')
      master.geometry()
      self.e = Entry(master)
      self.e.grid(row=0,column=0,columnspan=6,pady=3)
      self.e.focus_set() #Sets focus on the input text area
    
      # Generating Buttons
      Button(master,text="=",width=11,height=3, fg="red", bg="light green",command=lambda:self.equals()).grid(row=4, column=4,columnspan=2)
      Button(master,text='AC',width=5,height=3,fg="red", bg="light green", command=lambda:self.clearall()).grid(row=1, column=4)
      Button(master,text="+",width=5,height=3, fg="blue",bg="orange", command=lambda:self.action('+')).grid(row=4, column=3)
      Button(master,text="5",width=5,height=3, fg="blue",bg="orange", command=lambda:self.action(5)).grid(row=2, column=1)
      Button(master,text="3",width=5,height=3, fg="blue",bg="orange", command=lambda:self.action(3)).grid(row=3, column=2)

def start_application():
  root = Tk()
  app = calc(root)
  root.bind_class("Button", "<Button-1>", app.callback)
  return root

if __name__ == "__main__":
  start_application().mainloop()
Woden
  • 1,054
  • 2
  • 13
  • 26
  • Your link requires registration. – 8349697 Aug 16 '21 at 16:01
  • Thanks for reminding me, I think I'll bring all the code here later. – Woden Aug 16 '21 at 16:02
  • 2
    The button has an [invoke()](https://tcl.tk/man/tcl8.6/TkCmd/button.htm#M17) method. And you can also [generate an event](https://tcl.tk/man/tcl8.6/TkCmd/event.htm#M7). – 8349697 Aug 16 '21 at 16:08
  • Thank you @8349697 I'll give it a try, and I've uploaded the code. – Woden Aug 16 '21 at 16:23
  • The question remains the same that how to get the `Button` object? I've assigned the variable to the button, and called it in tests, but get `has no attribute` error. Trying to fix it. – Woden Aug 16 '21 at 16:31
  • You've posted way too much code. If the question about buttons, we don't need more than two dozen buttons. The solution that works for 2 will work for 20. Please try to reduce the code down to a [mcve]. – Bryan Oakley Aug 16 '21 at 18:33
  • Will do, thanks – Woden Aug 16 '21 at 18:34
  • The code has been reduced. Thank you. – Woden Aug 17 '21 at 03:26
  • 1
    Consider using the [tk widget as a parent class](https://docs.python.org/3/library/tkinter.html#a-simple-hello-world-program) for the `calc` class. If the `start_application` function returns the `app` itself, it will be easier to access the attributes of the `calc` class. Otherwise, you need to search for the buttons you want among `root.winfo_children()`. – 8349697 Aug 17 '21 at 22:30

0 Answers0