Action with multiple destinations can avoid code duplication
Lot of times, you may have an Action resulting in different destination contexts based on some business rules. Using the multi-destination feature can avoid logic duplication by utilizing the same action under multiple conditions. You can find some examples here.
Avoid duplicating same logic in multiple actions
If you find a situation where a set of statements (or logic) is duplicating in multiple actions, think about extracting this logic into a separate action. These statements may not make sense from a Scenario or Business Keyword point of view, but you can still extract this as a separate Action for the purpose of reusability.
You can call this action from several other actions as needed. With an ability to share an action, you can make use of this common action across multiple contexts also. This promotes reusability and greatly enhances the maintainability of test logic.
Here is an example, where an Action, "Verify Transfer Amount and Confirm" is calling "Verify Footer Links" as a called-Action. Footer link verification may be useful in many other Actions and hence that logic is centralized in one Action.
Local actions can eliminate clutter and improve re-usability
Many times, you may define Actions to be used internally in other Actions (Local Actions). such "called" actions may not be of significance from the Scenario point of view. It is best to turn-off Scenario visibility of such actions. This avoids clutter when you are building a scenario. Remember, you want to provide as clean a list of keywords to the Scenario developer as possible. Small and incremental practices like these have a significant impact on the overall clarity and maintainability.
Use functional Wait logic in Actions – avoid hard coded wait statements
It is a good practice to insert a synchronization statement (wait-*) at the beginning of every major action to make sure necessary pre-conditions on the screen are available. Many times, automation execution reliability is impacted due to timing issues. A statement in your action logic might attempt to act on an element before it is fully rendered on screen. In accelQ, you can insert wait statements based on element availability, state of enabled/not, attribute value, css attribute value etc (functional waits). Here is a detailed article.
When you insert a wait statement, depending on the need, you have an ability to abort test case if desired state is not achieved.
As much as possible, avoid hard coded fixed-time wait statements. In some cases though, it may be particularly tricky to determine the readiness of an element. If you do need to insert a hard-coded wait statement, limit it to a maximum of 5 seconds (after all other avenues with functional waits are exhausted).
Action sharing is a powerful feature that can avoid a lot of code duplication. Common navigation actions and business actions such as Logout are applicable in most pages in the application. Define these actions in one of the Contexts and share with rest. You will be managing element definition and other dependencies in a central place. Consider following two Scenarios.
Scenario 1: Login and Logout - Makes use of Logout functionality in Account Summary Page
Scenario 2: Update User Email - Makes use of Logout functionality from Profile Info Page
Perform Logout action can be defined in Account Summary Page and shared with the Profile Info Page.
Action should be independent
When building an action, do not make assumptions on which scenario will use it or what other actions would be called in the scenario before this action is called. Every action should be designed independent and get its own inputs and outputs. In other words, you should be able to plug in an action in different scenarios without dependency on other steps.
If an action needs to verify certain information, pass it as expected input. Do not rely on the order of other actions that may have been called before this. If there is information from an action that may be of interest to other actions in a scenario, it should be returned as an output parameter.
An action operates in a given context, performs its task by making use of the inputs passed to it, and returns appropriate outputs.