ODOOFX: Odoo library for Robot Framework
pip install robotframework-odoofx
OdooFxLibrary brings Odoo test automation to Robot Framework through two
channels under one keyword vocabulary: the Odoo web client, driven through
the Browser library, and Odoo's
external API (XML-RPC up to Odoo 18, JSON-2 from Odoo 19). A suite can cross
them: prepare or check data over the API, drive the screen for what you
actually test. It targets Odoo Community and Enterprise, and is proven live
on Odoo 16 and Odoo 19 Community (it is designed for Odoo 14 and later; 14,
15, 17 and 18 are not measured yet).
Status: alpha. Both channels work and are proven live on Odoo 16 and 19. Not covered yet: buttons that open a wizard, section and note lines, line deletion. Signatures may still change before 1.0.
Install
Requirements: Python 3.10 or later, Robot Framework 7.4 or later, and an Odoo
instance reachable over HTTP. The RPC channel needs nothing more. The web
extra (pip install "robotframework-odoofx[web]", then rfbrowser init once
for the Playwright browsers) brings the Browser library the web channel is
built on; all installs every extra.
Keyword documentation: cyrilm29.github.io/robotframework-odoofx.
Quick start
Over the API, reading a quotation's state:
*** Settings ***
Library OdooFxLibrary
*** Test Cases ***
Read A Quotation State
Connect To Odoo http://localhost:8069 my_database admin ${PASSWORD} channel=xmlrpc
Assert Quotation Status Is sale S00004
[Teardown] Disconnect From Odoo
On screen, entering a contact in the web client and reading it back independently:
*** Settings ***
Library Browser
Library OdooFxLibrary
*** Test Cases ***
Enter A Company Contact
${marker}= New Odoo Run Marker prefix=DEMO
New Browser chromium headless=${TRUE}
New Page about:blank
Log In To Odoo Web Client http://localhost:8069 my_database admin ${PASSWORD}
... expected_series=19 allow_write=${TRUE}
Navigate To Menu contacts.menu_contacts
Open New Odoo Web Form
Fill Odoo Web Field company_type company
Fill Odoo Web Field name ${marker} Company
${contact_id}= Save Odoo Web Record
${found}= Search Read Odoo Records Through Web Session res.partner
... ${{[["id", "=", $contact_id]]}} fields=${{["name"]}}
Should Be Equal ${found}[0][name] ${marker} Company
[Teardown] Run Keywords Run Keyword And Ignore Error Discard Odoo Web Changes
... AND Close Browser
Credentials never live in a suite: pass them on the command line
(robot -v "PASSWORD: Secret:..." suite.robot, the Robot Framework 7.4 typed
variable keeps them out of the logs) or through the environment.
How it behaves
- The target is proven first.
Get Odoo Server Versionneeds no session;Connect To OdooandLog In To Odoo Web Clientcheck the database and the Odoo series, andexpected_seriesrefuses another Odoo version before anything happens (two Odoo versions often run side by side). - Technical names and codes, never labels. Labels are translated and change
between versions. Menus are opened by external id (
sale.sale_menu_root), fields are named by technical name, a radio or selection option is given by its code (one, not its label), states are compared by code (draft,sent,sale,cancel). A label fails an assertion as "not a state code". - Writes are off unless you opt in (
allow_write), on both channels, and every write is journaled with its phase:planned,done,failedorunknown. Anunknownwrite (the answer was lost) is read back and reconciled, never sent again. - A value is read back as it is typed. A field the web client did not take fails the keyword at once. Relational values are picked among the client's proposals, never through Create or Search more, which would write or open a dialog.
- A form left half-typed is never saved by accident. Odoo saves a form it
leaves, so
Navigate To Menu,Go Back Through Odoo Web BreadcrumbandLog Out Of Odoo Web Clientrefuse to leave one with unsaved changes (save or discard first): a failed test never writes what it left behind.
Keywords
Connection, reads and state codes (RPC channel):
| Keyword | Purpose |
|---|---|
Connect To Odoo |
Prove the target (URL, database, version) and open the RPC connection; channel=auto picks JSON-2 from Odoo 19, XML-RPC before; allow_write opts in to writes, expected_series refuses another Odoo version |
Get Odoo Connection Info |
The proven target of the current connection, never the secret |
Count Odoo Records |
Count the records of a model matching a domain |
Search Read Odoo Records |
Read the records of a model matching a domain |
Get Odoo Record |
One identified record, whole or by fields |
Odoo Record Exists |
Whether a record exists (archived ones count) |
Odoo Record Should Exist, Odoo Record Should Not Exist |
Prove a presence or a removal on the identified record |
Get Field Selection Values |
The technical codes of a selection field |
Count Odoo Records By State |
Record count per state code |
Get Odoo Record State |
The state code of one identified record |
Odoo Record State Should Be |
Fail unless the identified record is in the expected state code |
Assert Quotation Status Is |
Check a quotation's state code, by order reference or id |
Get Odoo Config Parameter |
A system parameter such as web.base.url (administrator) |
Odoo User Should Be In Group, Odoo User Should Not Be In Group |
Group membership of a user, the group named by its external id |
Resolve Odoo External Id |
The database id behind an external id such as product.product_product_1, proved to be of the expected model |
Get Odoo Server Time |
The server's clock (UTC, one second), to bracket what a run created |
New Odoo Run Marker |
A unique label to put in a free-text field of the records a run creates, to find them again |
Disconnect From Odoo |
Close the connection |
Writes and their journal (both channels journal):
| Keyword | Purpose |
|---|---|
Create Odoo Record, Update Odoo Record, Delete Odoo Record |
Write a record (needs allow_write) |
Call Odoo Record Method |
Call a model method such as action_confirm or action_cancel (needs allow_write) |
Get Odoo Write Journal |
Every write (after entry since) with its phase: planned, done, failed or unknown (to read back, never to resend) |
Odoo Write Journal Should Hold No Unknown Outcome, Odoo Last Write Should Be |
Assert on the journal: no write of unknown effect, the last write in a given phase |
Reconcile Odoo Write |
Settle a planned or unknown journal entry after reading the record back |
Structure of the database (RPC channel):
| Keyword | Purpose |
|---|---|
Get Odoo Model Fields, Get Odoo Field Definition, Odoo Field Exists |
The fields of a model with their type, relation and inverse; one field, or whether it exists (fields come and go between versions) |
Get Odoo Model Relations |
The many2one, one2many and many2many fields of a model, with the model they point at |
Search Odoo Models |
The models whose technical name contains a text, wizards told apart |
Get Odoo Module State, Odoo Module Should Be Installed |
The state code of a module; fail unless installed |
Call Odoo Model Method |
Call a method that needs no record (name_search, search_count...); a method that may write needs allow_write and is journaled |
Web client: session, navigation and reads (built on the Browser library, which opens the browser, the context and the page):
| Keyword | Purpose |
|---|---|
Get Odoo Server Version |
The server's version and series, without session nor secret: the proof a suite opens with |
Log In To Odoo Web Client |
Log in through the login page of a named database; expected_series refuses another Odoo version before the page opens; the session's database and series are proven, the secret is never logged; allow_write opts in to screen writes |
Get Odoo Web Session Info, Odoo Web Session Is Open, Odoo Web Client Is Shown |
The target of the open web session; whether the session and the web client are still there |
Navigate To Menu |
Open an application, then its menus, by external ids (sale.sale_menu_root), never by label; waits for the view that replaces the one shown |
Get Odoo Web View, Get Odoo Web Current App |
The view shown (model, view type, action, record id) and the application of the menu bar |
Count Odoo Web View Records, Get Odoo Web Pager Total, Count Odoo Web Search Facets |
What the list or kanban shows: records (ghost cards excluded), the pager's total, the search facets |
Open Odoo Web Record, Get Odoo Web List Cell Text, Get Odoo Web Breadcrumb Depth, Go Back Through Odoo Web Breadcrumb |
Open a shown record in its form (by the cell of a named field), read a list cell, read and walk back the breadcrumb |
Get Odoo Web Statusbar Value, Odoo Web Form Should Be Saved |
The technical code of the form's current step; no pending change |
Count Odoo Records Through Web Session, Search Read Odoo Records Through Web Session |
Reads with the page's session cookie, on the same route from Odoo 16 to 19 |
Log Out Of Odoo Web Client |
Log out through the user menu |
Web client: form entry (screen writes need allow_write at the login and
are journaled):
| Keyword | Purpose |
|---|---|
Open New Odoo Web Form, Select Odoo Web Form Tab |
Open the form of a new record from a list or kanban; show a notebook page by its name |
Fill Odoo Web Field, Get Odoo Web Field Value |
Enter a value and read it back on the screen at once: text, dates (given as ISO, typed in the user's format), radio option and selection by code, checkbox, many2one and tags by exact name or by id; read what the form shows |
Save Odoo Web Record, Discard Odoo Web Changes, Get Odoo Web Invalid Fields, Odoo Web Form Is Unsaved |
Save and get the record id, or a refusal naming its cause; drop pending changes; the fields marked invalid; whether the form holds unsaved changes |
Add Odoo Web Line, Fill Odoo Web Line Field, Get Odoo Web Lines |
Add a line to a one2many field (the lines of a sales order), fill a field of the line in edition, read the lines once shown as stored |
Click Odoo Web Form Button |
Press a button that calls a method of the record (action_confirm), by its technical name; waits until Odoo answered and reloaded the record; needs allow_write, journaled |
Create New Quotation is a skeleton that only warns: do not rely on it.
Layout
src/OdooFxLibrary/ the library
Issues
Bug reports and suggestions are welcome as GitHub issues.
License
Apache 2.0. See LICENSE and NOTICE.
Trademarks
Odoo is a trademark of Odoo S.A. Robot Framework is a trademark of the Robot Framework Foundation. ODOOFX is an independent project, not affiliated with, sponsored or endorsed by Odoo S.A. or the Robot Framework Foundation.
Metadata
Release files for robotframework-odoofx 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| robotframework_odoofx-0.2.0.tar.gz | 48.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| robotframework_odoofx-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 99.7 kB
Release files / robotframework_odoofx-0.2.0.tar.gz
| Download URL | robotframework_odoofx-0.2.0.tar.gz |
|---|---|
| Size | 48.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c2e61169a809c44cf2ff165474d6b5d40caca71271d796114ec6e1a74a14f594
|
|
BLAKE2b-256 checksum How to use checksums |
ef2de5017fed5b43c2aa514dc00864ef64daba851cf3837ebaa0a09cd1da0625
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency logRelease files / robotframework_odoofx-0.2.0-py3-none-any.whl
| Download URL | robotframework_odoofx-0.2.0-py3-none-any.whl |
|---|---|
| Size | 51.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
480d70d867d36b5c393bc802a979a5a2e65b0c7f60ca622acbe0cf572ec74a83
|
|
BLAKE2b-256 checksum How to use checksums |
55073104ef0f5cc243490213e0ff13813a361dace79cdd9522c58a37f4a8bfc8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency log