Current situation
I'm developing a test-framework in python for end-to-end tests for a large ERP application. The AUT (application under test = ERP system) is structured in modules and provides a lot of different forms (e.g. to place an order), which need to be tested. The test-framework provides a high level API to execute some business processes (e.g. create a new order for a specific customer).
ERP System Architecture (AUT)
The GUI is a bunch of forms (more than 2000 differnt forms) and some tables or diagrams to display the data
Most forms have some basic buttons, that share some functionality across the whole system (e.g. close form, save form etc.), so there is a basic
Form
class which almost all other classes inherit from.A lot of forms in the sales module share some functionality, so there is a
SalesForm
class, which provides this specific functionality.Some forms which are in different modules share some functionality, this functionality is moved to it's own class and added with multiple inheritance.
Test Framework Architecture
There is also a demo of the current architecture available on repl.it.
Intention
I want to extend the Framework, to support customized variants of the ERP-System. This variants can change the structure of the dialogs or introduce additional steps in a business process. The idea is that if you want to test such a modified application, you create an "extension" to the framework, with the same structure as the "base", replacing specific methods with an alternative implementation.
Applying this to the demo on repl.it: I want to create a mytest2.py where I can overwrite the extension1.base.SomeExtension.new_feature() method, without breaking the rest of the architecture.
Expected outcome of mytest2.py
Test 2:
SalesOrder
called Form.basic_feature() from default
called SalesForm.sales_method() from default
called Form.basic_feature() from default
called SomeExtension.new_feature() from extension1 <-- important line
called SalesOrder.sales_order_method() from default
How would you approach this problem? Do you have any design patterns in mind and/or programming techniques that would be useful?
I currently tend to use the monkey patching ability of python to change the classes on runtime, by searching for available modules, classes, methods and so on. Order and dependencies of those extensions that need to patched would be stored in a dictionary. Monkey patching seem to be a pretty good fit for my use case...
BUT
Some people say monkey patching is evil and I tend to agree. Maybe there are better solutions for this problem, but I can not see them because I already have a (partial) solution already.
Requirements
I want to override a specific class method, this is mainly a problem if the class is deeper in the inheritance tree (e.g. I want to overwrite a method of the
Form
class.The order of plugins/extension does matter. E.g. you can have two extensions combined.
Doesn't break code completion in PyCharm IDE (but I am OK with instance level type hinting in a comment, so this shouln't be too complicated)
The general structure of the framework can be altered! Keeping the existing structure is only nice to have.