KEMBAR78
Odoo's Test Framework - Learn Best Practices | PPTX
A good test suite really is an
executable specification.
Test-driven development (TDD)
while not finished:
test = Test()
assert not test()
code.write()
assert test()
while test() and code.canBeBetter():
code.refactor()
Odoo’s test classes
● Inherit from unittest’s TestCase
● Manage a database cursor
● Performed on an Odoo database
TransactionCase
Each method is run
in its own
transaction.
The transaction
cursor is not
committed.
SavepointCase
All methods are run
in a single
transaction.
Each method is
guarded by a
savepoint (like a
sub-transaction).
setUpClass()
setUp()
test_method1()
tearDown()
setUp()
test_method2()
tearDown()
...
tearDownClass()
setUpClass()
setUp()
test_method1()
tearDown()
setUp()
test_method2()
tearDown()
...
tearDownClass()
HttpCase, HttpSavepointCase
● Launches
○ the HTTP server
○ a Chrome browser (headless)
● Open any URL
● Run some JavaScript in the Odoo client
Available on test instances
self.registry
self.cr
self.uid
self.env
Server-side form
● A Python implementation of a form
● Reproduces what the client-side form does
● Create or edit a record «just like a user
would»
Server-side form
move_form = Form(self.env['account.move'])
move_form.partner_id = partner
...
with move_form.invoice_line_ids.new() as line_form:
line_form.product_id = product
...
invoice = move_form.save()
Server-side form
with Form(invoice) as move_form:
move_form.partner_id = partner
...
with move_form.invoice_line_ids.edit(0) as line_form:
line_form.product_uom_id = uom
...
Use addCleanup()
def setUp(self):
super().setUp()
self.registry = odoo.registry(get_db_name())
self.addCleanup(self.registry.reset_changes)
self.addCleanup(self.registry.clear_caches)
#: current transaction's cursor
self.cr = self.cursor()
self.addCleanup(self.cr.close)
#: :class:`~odoo.api.Environment` for the current test case
self.env = api.Environment(self.cr, odoo.SUPERUSER_ID, {})
self.addCleanup(self.env.reset)
Use unittest.mock
from datetime import date
from unittest.mock import patch
day = date(1979, 5, 4)
with patch.object(fields.Date, 'today', lambda *args, **kwargs: day):
...
assert fields.Date.today() == day
...
Maximize coverage
● Every corner case
● Different users
● Access errors
● Expected errors
Thank You

Odoo's Test Framework - Learn Best Practices

  • 2.
    A good testsuite really is an executable specification.
  • 3.
    Test-driven development (TDD) whilenot finished: test = Test() assert not test() code.write() assert test() while test() and code.canBeBetter(): code.refactor()
  • 4.
    Odoo’s test classes ●Inherit from unittest’s TestCase ● Manage a database cursor ● Performed on an Odoo database
  • 5.
    TransactionCase Each method isrun in its own transaction. The transaction cursor is not committed. SavepointCase All methods are run in a single transaction. Each method is guarded by a savepoint (like a sub-transaction). setUpClass() setUp() test_method1() tearDown() setUp() test_method2() tearDown() ... tearDownClass() setUpClass() setUp() test_method1() tearDown() setUp() test_method2() tearDown() ... tearDownClass()
  • 6.
    HttpCase, HttpSavepointCase ● Launches ○the HTTP server ○ a Chrome browser (headless) ● Open any URL ● Run some JavaScript in the Odoo client
  • 7.
    Available on testinstances self.registry self.cr self.uid self.env
  • 8.
    Server-side form ● APython implementation of a form ● Reproduces what the client-side form does ● Create or edit a record «just like a user would»
  • 9.
    Server-side form move_form =Form(self.env['account.move']) move_form.partner_id = partner ... with move_form.invoice_line_ids.new() as line_form: line_form.product_id = product ... invoice = move_form.save()
  • 10.
    Server-side form with Form(invoice)as move_form: move_form.partner_id = partner ... with move_form.invoice_line_ids.edit(0) as line_form: line_form.product_uom_id = uom ...
  • 11.
    Use addCleanup() def setUp(self): super().setUp() self.registry= odoo.registry(get_db_name()) self.addCleanup(self.registry.reset_changes) self.addCleanup(self.registry.clear_caches) #: current transaction's cursor self.cr = self.cursor() self.addCleanup(self.cr.close) #: :class:`~odoo.api.Environment` for the current test case self.env = api.Environment(self.cr, odoo.SUPERUSER_ID, {}) self.addCleanup(self.env.reset)
  • 12.
    Use unittest.mock from datetimeimport date from unittest.mock import patch day = date(1979, 5, 4) with patch.object(fields.Date, 'today', lambda *args, **kwargs: day): ... assert fields.Date.today() == day ...
  • 13.
    Maximize coverage ● Everycorner case ● Different users ● Access errors ● Expected errors
  • 14.

Editor's Notes

  • #2 Welcome developers! This talk gives advice on best testing in Python. I am Raphael Collet, and I work as a developer. My job is to maintain and improve the Odoo server-side framework.
  • #3 Welcome developers! This talk gives advice on best testing in Python. I am Raphael Collet, and I work as a developer. My job is to maintain and improve the Odoo server-side framework.
  • #10 Since Odoo 12.0
  • #11 This invokes the form to create an invoice.
  • #13 Simpler to use than tearDown(). Also works inside test methods. There is addClassCleanup() for setUpClass() methods.
  • #14 Flexible enough for most fixtures. Automatically removed upon tearDown() Hand-written mockups often leave the system in a broken state.