Resource Files, Variable Files, Library Imports, and Project Layout: Configuration, Design Patterns, and Trade-Offs
Choose project-layout and import strategies deliberately: source-relative paths, project-root search paths, resource granularity, variable-file formats, Python packaging, dependency direction, and output placement.
Learning objectives
- Compare source-relative imports with one explicit project-root module-search contract.
- Choose resource-file granularity without creating a dependency maze.
- Choose among Robot resource variables, Python variable files, JSON, and optional YAML based on behavior and dependencies.
- Explain when a custom library should be packaged instead of injected with --pythonpath.
- Keep generated results outside source and avoid cycles/collisions as architecture rules.
Current compatibility baseline. Verified
2026-08-31: Robot Framework 7.4.2 is the current stable release and
requires Python 3.8+; 7.5b1 is a pre-release and is not required in
this chapter. In 7.4.2, .resource is the recommended
resource-file extension. Resource and Settings-level variable-file
paths are resolved relative to the importing data file first and
then via Python's module search path. ${CURDIR} is the
absolute directory of the current data file;
${EXECDIR} is the absolute directory where execution
started. JSON variable files require no optional package; YAML
variable files require PyYAML.
1. Configuration is architecture when it determines import identity
A path is not merely syntax. It decides who owns a dependency and
what must be true in every environment.
Resource ../resources/common.resource says the suite
and resource move together in one source tree.
Library company.robot.Common says a Python package must
be installed or discoverable.
--pythonpath project-root says the runner establishes a
source root. Choose the statement that matches the actual ownership
contract.
2. Decision table: path/import strategies
| Choice | Strength | Risk | Good default |
|---|---|---|---|
| Source-relative Resource/Variables path | Portable within one repository tree; independent of cwd | Long ../../ chains after deep nesting |
Use for nearby project-owned resource/data relationships |
Project-root --pythonpath |
Uniform module/resource lookup from many suites | Runner must define the root explicitly | Use for local project libraries or package-like resources |
| Installed Python package | Best reuse/versioning; no repo-specific path injection | Packaging/release overhead | Use for mature shared libraries |
| Absolute source path in test data | Unambiguous on one machine | Not portable across workstations/CI | Avoid except generated/local diagnostics |
| cwd-relative shell path | Convenient interactively | Changes with launcher/IDE/CI | Avoid as an implicit import contract |
3. Few large resources versus many tiny resources
A resource should group cohesive domain behavior, not every keyword
with a similar implementation type. One giant
common.resource creates broad coupling and name
collisions. Hundreds of one-keyword resources create import overhead
and navigation noise. Prefer a small graph whose direction is easy
to draw: suites → domain resources → shared utility resources →
libraries. A lower-level utility resource should not import a
higher-level domain resource back.
4. Choose the simplest variable source that preserves provenance
| Mechanism | Use when | Avoid when |
|---|---|---|
*** Variables *** in resource |
Small static values belong with the reusable keyword abstraction | Environment-specific or large structured configuration |
| Python variable file | Values need Python types, controlled computation, or arguments | Import-time I/O/random/network side effects are not essential |
| JSON variable file | Static structured data should be dependency-free | Humans need comments or advanced YAML features |
| YAML variable file | Human-friendly structured configuration is useful | You cannot explicitly depend on/pin PyYAML |
| CLI/environment variable | Deployment/CI supplies a run-specific value | The value should be committed as stable test data |
Robot variable precedence still applies. If multiple variable files define the same name, the earliest imported variable file wins, while variables from the suite Variable section and command-line variables have higher priority. Avoid “priority as configuration logic”; use distinct names or one authoritative source when possible.
5. Variable files should describe values, not orchestrate the world
A Python variable file can execute arbitrary Python during import. That makes it tempting to create directories, fetch tokens, contact APIs, or generate random test data there. The result is hidden setup whose failure appears as an import error and whose side effects happen before the test lifecycle is visible. Keep variable-file import deterministic and low-side-effect. Put external actions in explicit setup/user/library keywords where logs and cleanup ownership are visible.
6. --pythonpath versus installing a library package
--pythonpath is appropriate for a repository-local
library while it evolves with the tests. Once multiple repositories
depend on the same library, normal Python packaging gives you
versioned installation, dependency metadata, and consistent
discovery. Do not “publish a library” by asking every CI job to
append a developer-specific source directory to
PYTHONPATH.
7. Generated results belong outside source boundaries
# Good repository intent
results/
*.log
# keep tests/, resources/, variables/, libraries/, data/ as source/fixtures
Generated output.xml, log.html,
report.html, Libdoc HTML, screenshots, and debug files
are execution evidence. Put them under an explicit output root and
archive them according to CI retention policy. Writing them beside
labels.robot makes source trees dirty, risks accidental
commits, and complicates parallel workers.
8. Dependency cycles are an architecture smell even when imports appear to work
flowchart TD T[tests/] --> D[domain resources/] D --> U[utility resources/] D --> V[variables/] U --> L[libraries/] D --> F[data/] T --> O[results/] O -. generated evidence only .-> T
Keep the solid arrows one-way. If
utility.resource imports
domain.resource while the domain already imports the
utility, responsibilities are inverted. Break the shared behavior
into a lower-level resource/library that both can depend on rather
than hiding the cycle with dynamic imports.
9. Duplicate keyword names: qualify intentionally, then improve architecture
Robot's keyword scope priority resolves some conflicts, but two
imported resource files with the same keyword name remain ambiguous.
Full names such as orders.Normalize ID and
users.Normalize ID make the current behavior explicit.
If many calls require qualification, the project likely needs
clearer domain naming rather than a global search-order tweak.
Do not use Set Library Search Order as a blanket
architecture fix.
It can be useful for controlled runtime polymorphism, but global
priority changes make the source harder to read. Prefer explicit
names and cohesive resources.
10. Keep other configuration layers separate
| Layer | Example | Owner |
|---|---|---|
| Robot core execution | --pythonpath, --outputdir |
Robot runner contract |
| Python environment | venv, pinned packages | Project dependency management |
| External library config | Browser/API/database library arguments | Library/integration layer |
| System under test | App endpoint, fixture database | Test environment |
| Editor/RobotCode | Language server/project settings | Developer tooling, not execution truth |
| CI/container | Workspace mount, environment variables, artifact upload | Pipeline/runtime platform |
11. Worked scenario: choose a portable import contract
Three suites in tests/api/, tests/ui/, and
tests/ops/ need one repository-local Python library and
two domain resources. A reasonable design is:
-
Put the Python library under
libraries/and add the project root once with--pythonpath <root>. - Import domain resources with stable source-relative paths or package-like paths through the same root—pick one convention and document it.
-
Keep fixture paths anchored to the resource that owns them via
${CURDIR}. -
Write all results to an explicit root outside
tests/. - If the library becomes shared across repos, package and install it instead of expanding PYTHONPATH rules.
12. Knowledge check
When is ${CURDIR} usually stronger than ${EXECDIR}?
When a source file owns a nearby resource/data file and the relationship should remain valid regardless of where the process is launched.
What is the main trade-off of --pythonpath project-root?
It gives uniform local imports, but every runner must establish the same root explicitly. Mature cross-repository libraries are usually better installed as packages.
Why can Python variable files make CI failures hard to diagnose?
They execute at import time. Hidden I/O, randomness, or secret reads can fail before normal test lifecycle/logging and produce environment-dependent configuration.
Two resources import each other. What is the production design response?
Refactor the shared dependency downward into a third lower-level resource/library and restore a one-way graph rather than relying on the cycle.
13. Summary and next step
Project layout decisions are dependency contracts. Prefer source-relative relationships for nearby project files, an explicit module-search contract for repository-local Python code, installed packages for mature shared libraries, deterministic variable sources, and a separate output root. Lesson 4 diagnoses what happens when those rules fail: cycles, collisions, cwd dependence, module shadowing, optional dependency gaps, import-time secrets, and brittle path chains.
Further reading
- Robot Framework 7.4.2 User Guide — Resource and variable files — resource structure, import rules, Python/YAML/JSON variable files, and precedence.
- Robot Framework 7.4.2 User Guide — Using test libraries — importing libraries by name or path and library aliases.
-
Robot Framework 7.4.2 User Guide — Module search path
— installed packages, PYTHONPATH, and
--pythonpath. -
Robot Framework 7.4.2 User Guide — Built-in variables
—
${CURDIR},${EXECDIR}, path separators, and temporary directory. - Robot Framework 7.4.2 User Guide — Handling keywords with same names — scope priority, explicit qualification, and search order.
- Robot Framework 7.4.2 User Guide — Libdoc — library/resource documentation and import verification.
- Robot Framework 7.4.2 on PyPI — pinned stable package metadata and Python requirement.
- Robot Framework releases — re-check stable/pre-release status when updating the chapter.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.