Reputation: 476
I am writing a test automation framework for a GUI application and I wanted to use decorators as a way to catch pop-ups that are generated by methods within the class (for example login)
I have a _BaseWindow
class that keeps track of the elements of the GUI that are in every window (eg: menu bar, popups), which is inherited by a MainWindow
class. The MainWindow
class keeps track of buttons on the main menu, as well the dialog generated when one of the buttons is clicked. For example, if you click the login button on the main menu, the login dialog is loaded.
class _BaseWindow(object):
def __init__(self):
self.window = "windowName"
self.popup = None
def _catch_popups(self, method):
from functools import wraps
@wraps(method)
def wrapper(*args, **kwargs):
# get a list of the open windows before the method is run
before = getwindowlist()
retval = method(*args, **kwargs)
# get a list of the open windows after the method is run
after = getwindowlist()
for window in after:
if window not in before:
self.popup = window
break
return retval
return wrapper
class MainWindow(_BaseWindow):
def __init__(self):
self.dialog = None
self.logged_in = False
# buttons
self.login_button = "btnLogin"
super(MainWindow, self).__init__()
def click_login(self):
if not self.dialog:
mouseclick(self.window, self.login_button)
self.dialog = LoginDialog()
class LoginDialog(_BaseWindow):
def __init__(self):
# buttons
self.button_ok = "btnLoginOK"
self.button_cancel = "btnLoginCancel"
# text input
self.input_username = "txtLoginUsername"
self.input_password = "txtLoginPassword"
super(LoginDialog, self).__init__()
@MainWindow._catch_popups
def perform_login(self, username, password):
input_text(self.input_username, username)
input_text(self.input_password, password)
mouseclick(self.window, self.button_ok)
When I try to test this I get:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "objects/gui.py", line 185, in <module>
class LoginDialog(_BaseWindow):
File "objects/gui.py", line 236, in LoginDialog
@MainWindow._catch_popups
TypeError: unbound method _catch_popups() must be called with
MainWindow instance as first argument (got function instance instead)
When I try to specify the MainWindow instance
as:
@MainWindow._catch_popups(self)
I get:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "objects/gui.py", line 185, in <module>
class LoginDialog(_BaseWindow):
File "objects/gui.py", line 236, in LoginDialog
@MainWindow._catch_popups(self)
NameError: name 'self' is not defined
Any help in understanding this would be most appreciated.
Upvotes: 1
Views: 507
Reputation: 9010
There's an error in your structure here. Instance methods that are defined in the class body are created at the same time as the class and, as such, are unbound methods. Thus, at the time of creation, there is no self
to reference. Your _catch_popups
method requires an instance.
The natural solution is to change _catch_popups
to be a staticmethod
that returns an instance method.
@staticmethod
def _catch_popups(method):
from functools import wraps
@wraps(method)
def wrapper(self, *args, **kwargs):
before = getwindowlist()
retval = method(self, *args, **kwargs) # notice that self is passed
after = getwindowlist()
try: self.popup = next(w for w in after if w not in before)
return retval
return wrapper
Here, self
is explicitly passed to method
because it is still an unbound instance method when it is being passed to the decorator.
As suggested in your comment, you want to add the popup to the window instance. To do this you could update the following methods:
def click_login(self):
if not self.dialog:
mouseclick(self.window, self.login_button)
self.dialog = LoginDialog(self) # pass window to dialog __init__
class LoginDialog(_BaseWindow):
def __init__(self, parent_window):
# original code
self.parent = parent_window
@staticmethod
def _catch_popups(method):
def wrapper(self, *args, **kwargs):
# original code
try: self.parent.popup = ... # add to parent rather than self
return retval
return wrapper
Upvotes: 3