Skip to content

Avoiding a Global Resource File

In many Robot Framework projects, there is a temptation to simplify setup by consolidating all resources and libraries into one global file—often named something like Keywords.resource—which is then usually the only resource imported into suites and other resource files.

Executive Summary: While global resource files might seem convenient initially, they lead to circular dependencies, keyword ambiguities, performance issues, and maintenance challenges. This guide explains the problems of this approach and offers a modular alternative that improves code organization, maintainability, and performance.

  • Circular Dependencies: Using one global file in multiple keyword resource files can create circular dependencies. While Robot Framework does not display warnings during execution, RobotCode flags these issues with information-level diagnostics about already imported files or circular references. These diagnostics highlight poor design practices that complicate dependency management, hinder refactoring, and reduce modularity. Tightly coupled components can lead to cascading errors when changes are made.

  • Ambiguities in Keyword Resolution and Variable Precedence: In Robot Framework, keywords are not silently overwritten. Instead, if multiple keywords with the same name are present, test execution fails due to ambiguous matches. This forces test writers to explicitly decide which resource's keyword to use, thereby increasing the effort during test case creation. Additionally, variables behave differently: the first variable declaration takes precedence, and all subsequent declarations in other variable sections from different resource files are ignored. Consequently, when using a global resource file, you must carefully manage the order in which resources are imported to ensure the intended variable values are used—further adding to the overall complexity.

  • Performance Issues: For every keyword call, Robot Framework looks the name up in the imported libraries and resource files—and code analysis tools like RobotCode must do the same. Within each of them, normal keywords are found by name through an index, but keywords with embedded arguments are checked one by one, which requires regular expressions rather than simple string comparisons. A large global file filled with rarely used elements, especially keywords with embedded arguments, can slow down keyword resolution, particularly in larger projects.

    Heuristic: If a global resource file contains more than ~200 keywords or imports more than ~20 resource files, consider modularizing to improve performance and maintainability.

  • Decreased Maintainability: While the number of resource files may be relatively small—perhaps 50 to 100—the combined global resource file can easily contain 500 or more keywords. This consolidation makes it difficult for test writers to quickly locate the relevant keyword or variable among hundreds of entries. The dense aggregation reduces clarity and increases the maintenance burden, as even a small change might affect multiple areas of the project. Refactoring becomes more challenging when the entire functionality is bundled into a single file, since updates or corrections must be made with care to avoid unintended side effects across the project.

  • Creation of Unnecessary References: Relying on a centralized file forces all suites and resource files to reference the same global file, even if they only need a subset of its contents. This means that every suite is indirectly coupled to every keyword and variable in that file, regardless of whether they are used. Such an arrangement makes it difficult to determine which keywords are actually needed for a given suite. When updates or refactoring are required, developers may unintentionally modify or remove elements that other parts of the project still depend on. This extra layer of indirection complicates resource tracking, increases the likelihood of errors during maintenance, and can create confusion in larger teams where multiple developers modify the global file simultaneously.

Example of Problematic Global Resource File

Section titled “Example of Problematic Global Resource File”

Consider this example of a typical global resource file that causes issues:

*** Settings ***
# Global.resource - tries to do everything
Library SeleniumLibrary
Library RequestsLibrary
Library DatabaseLibrary
Library OperatingSystem
Resource LoginKeywords.resource
Resource CustomerKeywords.resource
Resource OrderKeywords.resource
Resource ApiKeywords.resource
Resource ReportingKeywords.resource
# ...and 20+ more imports
*** Variables ***
${GLOBAL_URL} https://example.com
${DB_CONNECTION} connection_string
# ...hundreds of variables for different modules
*** Keywords ***
# 500+ keywords covering every aspect of the system

Note: importing the same library with different parameters in different places can lead to later imports being ignored—Robot Framework keeps the first one—causing surprising behavior. Robot Framework 7.4 and newer warn about such an import, and RobotCode then reports it as LibraryImportIgnored. Avoid parameterized duplicates and prefer small, purpose-specific imports.

When this file gets imported into multiple other resources and suite files, it creates a tangled web of dependencies.

Suite settings do more than just configure your test environment—they serve as essential documentation for your project. When you explicitly declare libraries, resources and also variables in your settings section, you're creating a clear record of which technical or functional areas of the application are being used in this file. This transparency helps team members, stakeholders, and testers quickly understand the suite's purpose.

For example:

*** Settings ***
Library LoginProcess
Resource CustomerManagement.resource
Resource DatabaseValidation.resource

This declaration immediately communicates that this suite deals with login processes, customer management, and database validation—improving maintainability and knowledge transfer within your team.

Limitations in Import and Package Management

Section titled “Limitations in Import and Package Management”

Robot Framework’s import mechanism is quite basic—it lacks namespaces or similar constructs found in other programming languages. Instead, it relies solely on a name alias for libraries or the resource file name. Although Robot Framework does not flag multiple imports as errors, this simplicity can lead to issues. For instance, importing the same library with different parameters may result in the later import being ignored, causing unpredictable behavior. This limitation is often discussed in roadmap conversations, with hopes that future versions of Robot Framework will include more robust support for modular imports and package management.

Adhering to clean code principles is crucial for building maintainable, readable, and scalable projects. A modular approach offers several benefits:

  • Separation of Concerns: Grouping resources into logically separated files ensures each file has a clear, focused purpose, making the codebase easier to understand and maintain.

  • Enhanced Readability: Smaller, purpose-driven files improve readability, enabling developers to quickly locate and modify only the necessary parts of the code.

  • Simplified Dependency Management: Reducing inter-file references decreases coupling. This separation limits the impact of changes and makes your project more resilient.

  • Ease of Refactoring: When resources are organized into well-defined modules, refactoring becomes more straightforward. Developers can update or replace specific components without unintended side effects on unrelated parts.

The recommended solution is to modularize your resources:

  • Keep the Global File Minimal: Restrict the global resource file to only those libraries and resources that are truly needed across all test cases and keyword files.

  • Import Only What’s Needed: Instead of centralizing everything, selectively import only the necessary resources into each test case or keyword file.

  • Organize Resources by Function: Structure your project by grouping resources into files based on their function. For example, separate business-specific keywords from technical ones (such as those for database or API interactions).

This modular approach not only eliminates issues like circular dependencies and performance bottlenecks but also enhances maintainability and clarity. It ensures that suite settings clearly document which components are required for each test suite.

A well-organized Robot Framework project might have a structure like this:

project/
├── lib/
│ └── UserData.py
├── resources/
│ ├── api/
│ │ ├── authentication.resource # API auth keywords
│ │ ├── customers.resource # Customer API endpoints
│ │ └── orders.resource # Order API endpoints
│ ├── functional/
│ │ ├── users.resource # User domain keywords
│ │ └── customers.resource # Customer domain keywords
│ ├── ui/
│ │ ├── login.resource # Login page interactions
│ │ ├── customers.resource # Customer page interactions
│ │ └── common_elements.resource # Shared UI elements
│ └── common/
│ ├── test_data.resource # Test data generation
│ └── utility.resource # General helper keywords
└── tests/
├── api/
│ └── customer_api_tests.robot # Imports only api/customers.resource
├── business/
│ └── contracts.robot # Imports functional/* and common/test_data
└── ui_tests/
└── login_tests.robot # Imports ui/login, ui/customers and common/test_data

In this structure, each test file imports only the specific resources it needs, avoiding a global import file. Note: Robot Framework resolves Resource imports relative to the importing file (or via absolute paths). A resource it doesn't find there is searched in the module search path, so the imports below work once resources/ is on the Python path (--pythonpath resources, or python-path = ["resources"] in robot.toml, which RobotCode's analysis uses too).

Your suite settings can look like this:

login_tests.robot
*** Settings ***
Resource ui/login.resource
Resource ui/customers.resource
Resource common/test_data.resource

and if you have a suite for functional tests, it can look like this:

contracts.robot
*** Settings ***
Resource functional/users.resource
Resource functional/customers.resource
Resource common/test_data.resource

Migration Guide: From Global to Modular Structure

Section titled “Migration Guide: From Global to Modular Structure”

If you have an existing project with a large global resource file, consider this incremental approach:

  1. Analyze usage patterns:

    • Identify which keywords/variables are actually used in each test suite
    • Look for natural functional groupings (UI, API, data generation, etc.)
    • Measure counts (keywords, variables, and resource imports) per file to prioritize refactoring; use a threshold (for example ~200 keywords) as a guide.
  2. Create specialized resource files:

    • Start with one functional area (e.g., login functionality)
    • Extract relevant keywords into a new login.resource file
    • Maintain the original global file temporarily
  3. Gradual transition:

    • Update one test suite at a time to use the specific resource
    • Keep the global import during transition for backward compatibility
    • Run tests after each change to verify functionality
  4. Progressive cleanup:

    • Once all suites using specific functionality import the correct resource file
    • Remove those keywords from the global file
    • Eventually phase out the global file completely

Here's a concrete example of refactoring from a global approach to a modular one:

Before (Global.resource):

*** Settings ***
# Anti-pattern: single global import
Resource resources/Global.resource
*** Test Cases ***
Valid Login
Login To Application valid_user valid_password
Get Customer Details 123

After:

*** Settings ***
# Only importing what is needed
Resource ui/login.resource
Resource api/customers.resource
*** Test Cases ***
Valid Login
Login To Application valid_user valid_password
Get Customer Details 123

If restructuring your project isn't an option, you can mitigate potential issues by managing diagnostics from your development environment. For example, you can suppress diagnostics related to circular dependencies and redundant imports on a per-file basis or globally.

It's important to understand that suppressing warnings doesn't fix the underlying issues—it merely acknowledges them. When you choose to ignore specific diagnostics, you're making a conscious decision that these particular issues are acceptable in your codebase. This explicit acknowledgment is different from simply ignoring random warnings in your IDE. By documenting suppressions in your code or configuration files, you're communicating to your team that you understand the potential problem but have determined that it's an acceptable compromise given your project's constraints.

This approach is particularly useful during transitional periods or when working with legacy codebases where perfect architectural solutions aren't immediately feasible. However, these suppressions should ideally be revisited periodically to determine if the underlying issues can eventually be resolved through refactoring.

Use directives to disable diagnostics for circular dependencies and already-imported resources on a per-file basis.

# example for disabling of specific messages for the whole file
# robotcode: ignore[ResourceAlreadyImported, PossibleCircularImport]
*** Settings ***
Variables variables
# example for disabling of specific messages for a statement
Resource already_imported.resource # robotcode: ignore[ResourceAlreadyImported]
Resource circular_import.resource # robotcode: ignore[PossibleCircularImport]

The preferred approach to suppress diagnostics globally is using a robot.toml configuration file. This method is IDE-independent and can be checked into version control to share with your team. Create a robot.toml file in your project root with these contents:

[tool.robotcode-analyze.modifiers]
ignore = [
"PossibleCircularImport",
"CircularImport",
"ResourceAlreadyImported",
"VariablesAlreadyImported",
"LibraryAlreadyImported"
]

Alternatively, if you're working exclusively in VS Code, you can add the following to your settings.json, though this approach is not recommended for team environments as it only affects your local setup:

"robotcode.analysis.diagnosticModifiers.ignore": [
"PossibleCircularImport",
"CircularImport",
"ResourceAlreadyImported",
"VariablesAlreadyImported",
"LibraryAlreadyImported"
]

In summary, while a single global resource file might simplify the initial setup by reducing the number of imports, it ultimately creates more problems than it solves. Issues such as circular dependencies, naming collisions, performance degradation, and decreased maintainability quickly outweigh the initial convenience. A modular resource structure adheres to clean code principles and ensures that suite settings serve as clear, documented indicators of which parts of the application are under test. If a centralized file is unavoidable, selectively suppressing warnings can help manage the associated risks.

Released under the Apache-2.0. Copyright © 2022-present Daniel Biehl