Тестування Odoo¶
Є багато способів перевірити додаток. В Odoo ми маємо три види тестів
Модульні тести Python (див. Тестування коду Python): корисні для тестування бізнес-логіки моделі
Модульні тести JS (див. Тестування коду JS): корисні для ізольованого тестування коду javascript
Тури (див. Інтеграційне тестування): тури імітують реальну ситуацію. Вони гарантують, що частини python і javascript належним чином спілкуються одна з одною.
Тестування коду Python¶
Odoo надає підтримку для тестування модулів за допомогою unittest.
Щоб написати тести, просто визначте підпакет tests
у своєму модулі, він автоматично перевірятиметься на тестові модулі. Тестові модулі повинні мати назву, яка починатиметься з test_
, і їх слід імпортувати з tests/__init__.py
, напр.
your_module
├── ...
├── tests
| ├── __init__.py
| ├── test_bar.py
| └── test_foo.py
і __init__.py
містить:
from . import test_foo, test_bar
Попередження
тестові модулі, які не імпортовані з tests/__init__.py
, не запускатимуться
Програма тестування просто запустить будь-який тестовий приклад, як описано в офіційній документації unittest, але Odoo надає ряд утиліт і помічників, пов’язаних з тестуванням вмісту Odoo (головним чином модулів):
- class odoo.tests.common.TransactionCase(methodName='runTest')[source]¶
TestCase, у якому кожен тестовий метод виконується у власній транзакції та з власним курсором. Транзакція відкочується, а курсор закривається після кожного тесту.
- browse_ref(xid)[source]¶
Повертає об’єкт запису для наданого зовнішнього ідентифікатора
- Параметри
xid – повний зовнішній ідентифікатор у формі
module.identifier
- Raise
ValueError, якщо не знайдено
- Повертає
- ref(xid)[source]¶
Повертає ID бази даних для наданого зовнішнього ідентифікатора, ярлик для
get_object_reference
- Параметри
xid – повний зовнішній ідентифікатор у формі
module.identifier
- Raise
ValueError, якщо не знайдено
- Повертає
зареєстрований id
- class odoo.tests.common.SingleTransactionCase(methodName='runTest')[source]¶
TestCase, у якому всі тестові методи виконуються в одній транзакції, транзакція починається з першого тестового методу та відкочується в кінці останнього.
- browse_ref(xid)[source]¶
Повертає об’єкт запису для наданого зовнішнього ідентифікатора
- Параметри
xid – повний зовнішній ідентифікатор у формі
module.identifier
- Raise
ValueError, якщо не знайдено
- Повертає
- ref(xid)[source]¶
Повертає ID бази даних для наданого зовнішнього ідентифікатора, ярлик для
get_object_reference
- Параметри
xid – повний зовнішній ідентифікатор у формі
module.identifier
- Raise
ValueError, якщо не знайдено
- Повертає
зареєстрований id
- class odoo.tests.common.SavepointCase(methodName='runTest')[source]¶
Подібно до
SingleTransactionCase
тим, що всі методи тестування виконуються в одній транзакції але кожен тестовий приклад виконується всередині точки збереження з відкатом (суб-транзакція).Корисно для тестів, які містять швидкі тести, але зі значними налаштуваннями бази даних, спільними для всіх випадків (складні тестові дані в базі даних):
setUpClass()
можна використовувати для генерації тестових даних бази даних один раз, а потім усі тести використовують однакові дані, не впливаючи один на одного, але й без необхідності відтворювати тестові дані.
- class odoo.tests.common.HttpCase(methodName='runTest')[source]¶
Транзакційний HTTP TestCase з url_open і безголовими помічниками Chrome.
- browse_ref(xid)[source]¶
Повертає об’єкт запису для наданого зовнішнього ідентифікатора
- Параметри
xid – повний зовнішній ідентифікатор у формі
module.identifier
- Raise
ValueError, якщо не знайдено
- Повертає
- browser_js(url_path, code, ready='', login=None, timeout=60, **kw)[source]¶
Перевірте код js, запущений у браузері - необов’язково увійдіть як „увійти“ - завантажте сторінку, надану url_path - зачекайте, поки готовий об’єкт стане доступним - eval(code) всередині сторінки
Щоб повідомити про успішний тест, виконайте: console.log(„test успішно“) Щоб повідомити про збій тесту, створіть виняток або викличте console.error
- ref(xid)[source]¶
Повертає ID бази даних для наданого зовнішнього ідентифікатора, ярлик для
get_object_reference
- Параметри
xid – повний зовнішній ідентифікатор у формі
module.identifier
- Raise
ValueError, якщо не знайдено
- Повертає
зареєстрований id
- odoo.tests.common.tagged(*tags)[source]¶
Декоратор для позначення тегами об’єктів BaseCase. Теги зберігаються в наборі, доступ до якого можна отримати за допомогою атрибута test_tags. Тег із префіксом „-„ видаляє тег, наприклад. щоб видалити тег „standard“. За замовчуванням усі тестові класи з odoo.tests.common мають атрибут test_tags, який за умовчанням має значення „standard“ і „at_install“. При використанні успадкування класів теги НЕ успадковуються.
За замовчуванням тести запускаються один раз відразу після встановлення відповідного модуля. Тестові приклади також можна налаштувати для запуску після встановлення всіх модулів, а не для запуску одразу після встановлення модуля:
# coding: utf-8
from odoo.tests import HttpCase, tagged
# This test should only be executed after all modules have been installed.
@tagged('-at_install', 'post_install')
class WebsiteVisitorTests(HttpCase):
def test_create_visitor_on_tracked_page(self):
Page = self.env['website.page']
Найпоширенішою ситуацією є використання TransactionCase
і перевірка властивості моделі в кожному методі:
class TestModelA(common.TransactionCase):
def test_some_action(self):
record = self.env['model.a'].create({'field': 'value'})
record.some_action()
self.assertEqual(
record.field,
expected_field_value)
# other tests...
Примітка
Методи тестування мають починатися з test_
- class odoo.tests.common.Form(recordp, view=None)[source]¶
Реалізація представлення форми на стороні сервера (частково)
Реалізує більшу частину потоку маніпуляцій «представлення форми», щоб тести на стороні сервера могли більш правильно відображати поведінку, яка спостерігалася б під час маніпулювання інтерфейсом:
виклик default_get і відповідні зміни під час «створення»
виклик відповідних змін у полях налаштування
належним чином обробляти значення за замовчуванням і зміни навколо полів x2many
Збереження форми повертає створений запис у режимі створення.
Звичайні поля можна просто призначити безпосередньо до форми, для полів
Many2one
призначається єдиний набір записів:# empty recordset => creation mode f = Form(self.env['sale.order']) f.partner_id = a_partner so = f.save()
Під час редагування запису використання форми як менеджера контексту для автоматичного збереження в кінці області:
with Form(so) as f2: f2.payment_term_id = env.ref('account.account_payment_term_15days') # f2 is saved here
Для полів
Many2many
саме поле єM2MProxy
і може бути змінено додаванням або видаленням записів:with Form(user) as u: u.groups_id.add(env.ref('account.group_account_manager')) u.groups_id.remove(id=env.ref('base.group_portal').id)
Нарешті
One2many
реифікуються якO2MProxy
.Оскільки
One2many
існує лише через батьківського елемента, ним маніпулюють більш безпосередньо, створюючи «підформи» за допомогоюnew()
іedit()
методи. Зазвичай вони використовуються як контекстні менеджери, оскільки вони зберігаються в батьківському записі:with Form(so) as f3: # add support with f3.order_line.new() as line: line.product_id = env.ref('product.product_product_2') # add a computer with f3.order_line.new() as line: line.product_id = env.ref('product.product_product_3') # we actually want 5 computers with f3.order_line.edit(1) as line: line.product_uom_qty = 5 # remove support f3.order_line.remove(index=0) # SO is saved here
- Параметри
recordp (odoo.models.Model) – порожній або єдиний набір записів. Порожній набір записів переведе подання в режим «створення» та ініціює виклики default_get і on-load onchanges, singleton переведе його в режим «редагування» та завантажить лише дані представлення.
view (int | str | odoo.model.Model) – id, xmlid або фактичний об’єкт перегляду для використання для змін і обмежень представлення. Якщо жодного не надано, просто завантажується типове представлення для моделі.
Нове в версії 12.0.
- save()[source]¶
Зберігає форму, повертає створений запис, якщо це можливо
не зберігає поля
лише для читання
не зберігає незмінені поля (під час редагування) — будь-яке призначення або повернення onchange позначає поле як змінене, навіть якщо встановлено його поточне значення
- Викликає
AssertionError – якщо у формі є незаповнені обов’язкові поля
- class odoo.tests.common.M2MProxy[source]¶
Поводиться як
Sequence
наборів записів, які можна індексувати або нарізати, щоб отримати фактичні основні набори записів.
- class odoo.tests.common.O2MProxy[source]¶
- edit(index)[source]¶
Повертає
Форму
для редагування вже існуючого записуOne2many
.Форма створюється з представлення списку, якщо його можна редагувати, або з представлення форми поля в іншому випадку.
- Викликає
AssertionError – якщо поле не редагується
- new()[source]¶
Повертає
Форму
для нового записуOne2many
, правильно ініціалізованого.Форма створюється з представлення списку, якщо його можна редагувати, або з представлення форми поля в іншому випадку.
- Викликає
AssertionError – якщо поле не редагується
- remove(index)[source]¶
Видаляє запис в
index
з батьківської форми.- Викликає
AssertionError – якщо поле не редагується
Запуск тестів¶
Тести запускаються автоматично під час встановлення або оновлення модулів, якщо --test-enable
було ввімкнено під час запуску сервера Odoo.
Вибір тесту¶
В Odoo тести Python можна позначати тегами, щоб полегшити вибір тестів під час виконання тестів.
Підкласи odoo.tests.common.BaseCase
(зазвичай через TransactionCase
, SavepointCase
або HttpCase
) автоматично позначаються тегами standard
і at_install
за замовчуванням.
Виклик¶
--test-tags
можна використовувати для вибору/фільтрування тестів для запуску в командному рядку. Це передбачає --test-enable
, тому немає необхідності вказувати --test-enable
, коли використовуючи --test-tags
.
Цей параметр за замовчуванням має значення +standard
, що означає, що тести з тегом standard
(явно чи неявно) виконуватимуться за замовчуванням під час запуску Odoo за допомогою --test-enable
.
Під час написання тестів декоратор tagged()
можна використовувати в тестових класах, щоб додавати або видаляти теги.
Аргументами декоратора є назви тегів у вигляді рядків.
Небезпека
tagged()
є декоратором класу, він не впливає на функції чи методи
Щоб видалити теги замість того, щоб додавати або вибирати їх, наприклад. якщо ви не хочете, щоб ваш тест виконувався за замовчуванням, ви можете видалити тег standard
:
from odoo.tests import TransactionCase, tagged
@tagged('-standard', 'nice')
class NiceTest(TransactionCase):
...
Цей тест не буде вибрано за замовчуванням, щоб запустити відповідний тег потрібно буде вибрати явно:
$ odoo-bin --test-tags nice
Зауважте, що виконуватимуться лише тести з тегом nice
. Щоб запустити як ``both``, так і ``standard`` тести, надайте кілька значень :option:`–test-tags <odoo-bin –test-tags>`: у командному рядку значення є *additive (ви вибираєте всі тести з any із зазначених тегів)
$ odoo-bin --test-tags nice,standard
Параметр config switch також приймає префікси +
і -
. Префікс +
мається на увазі, тому він абсолютно необов’язковий. Префікс -
(мінус) призначений для скасування вибору тестів, позначених префіксованими тегами, навіть якщо вони вибрані іншими вказаними тегами, наприклад. якщо є standard
тести, які також позначені як slow
, ви можете запустити всі стандартні тести except повільних:
$ odoo-bin --test-tags 'standard,-slow'
Коли ви пишете тест, який не успадковує BaseCase
, цей тест не матиме тегів за замовчуванням, ви повинні додати їх явно, щоб включити тест до набору тестів за замовчуванням . Це поширена проблема під час використання простого unittest.TestCase
, оскільки вони не запускаються:
import unittest
from odoo.tests import tagged
@tagged('standard', 'at_install')
class SmallTest(unittest.TestCase):
...
Окрім тегів, ви також можете вказати конкретні модулі, класи або функції для тестування. Повний синтаксис формату, який приймає --test-tags
є:
[-][tag][/module][:class][.method]
Отже, якщо ви хочете протестувати модуль stock_account
, ви можете використовувати:
$ odoo-bin --test-tags /stock_account
Якщо ви хочете протестувати певну функцію з унікальною назвою, його можна вказати безпосередньо:
$ odoo-bin --test-tags .test_supplier_invoice_forwarded_by_internal_user_without_supplier
Це еквівалентно
$ odoo-bin --test-tags /account:TestAccountIncomingSupplierInvoice.test_supplier_invoice_forwarded_by_internal_user_without_supplier
якщо назва тесту однозначна. Кілька модулів, класів і функцій можна вказати одночасно, розділивши їх символом ,
, як у звичайних тегах.
Приклади¶
Важливо
Тести будуть виконуватися тільки в установлених модулях. Якщо ви починаєте з чистої бази даних, вам потрібно принаймні один раз встановити модулі з перемикачем -i
. Після цього він більше не потрібен, якщо тільки вам не потрібно оновити модуль, у такому випадку можна використовувати -u
. Для простоти ці перемикачі не вказані в наведених нижче прикладах.
Запустіть лише тести з модуля продажу:
$ odoo-bin --test-tags /sale
Запустіть тести з модуля продажу, але не ті, які позначені як повільні:
$ odoo-bin --test-tags '/sale,-slow'
Виконуйте лише ті тести, які є на складі або позначені як повільні:
$ odoo-bin --test-tags '-standard, slow, /stock'
Примітка
-standard
неявний (не обов’язковий) і представлений для ясності
Тестування коду JS¶
Тестування складної системи є важливим запобіжним заходом для запобігання регресії та гарантії, що деякі базові функції все ще працюють. Оскільки Odoo має нетривіальну кодову базу в Javascript, її необхідно перевірити. У цьому розділі ми обговоримо практику тестування JS-коду окремо: ці тести залишаються в браузері й не повинні досягати сервера.
Набір тестів Qunit¶
Фреймворк Odoo використовує фреймворк для тестування бібліотеки QUnit як засіб виконання тестів. QUnit визначає поняття tests і modules (набору пов’язаних тестів) і надає нам веб-інтерфейс для виконання тестів.
Наприклад, ось як може виглядати тест pyUtils:
QUnit.module('py_utils');
QUnit.test('simple arithmetic', function (assert) {
assert.expect(2);
var result = pyUtils.py_eval("1 + 2");
assert.strictEqual(result, 3, "should properly evaluate sum");
result = pyUtils.py_eval("42 % 5");
assert.strictEqual(result, 2, "should properly evaluate modulo operator");
});
Основний спосіб запустити набір тестів - мати запущений сервер Odoo, а потім перейти у веб-браузері до /web/tests
. Потім набір тестів буде виконано механізмом Javascript веб-браузера.

Веб-інтерфейс користувача має багато корисних функцій: він може запускати лише деякі підмодулі або фільтрувати тести, які відповідають рядку. Він може показати всі твердження, невдалі чи пройдені, повторно запустити певні тести, …
Попередження
Під час роботи набору тестів переконайтеся, що:
вікно браузера сфокусовано,
він не збільшений/зменшений. Він повинен мати рівно 100% масштабу.
Якщо це не так, деякі тести будуть невдалими без належного пояснення.
Тестування інфраструктури¶
Ось короткий огляд найважливіших частин інфраструктури тестування:
є набір ресурсів під назвою web.qunit_suite. Цей набір містить основний код (загальні активи + бекенд активів), деякі бібліотеки, засіб виконання тестів QUnit і тестові пакети, перелічені нижче.
пакет під назвою web.tests_assets включає більшість активів і утиліт, необхідних для набору тестів: користувальницькі підтвердження QUnit, помічники тестування, активи з відкладеним завантаженням тощо.
інший комплект активів, web.qunit_suite_tests, містить усі тестові сценарії. Зазвичай це місце, де тестові файли додаються до набору.
у web є controller, зіставлений з маршрутом /web/tests. Цей контролер просто рендерить шаблон web.qunit_suite.
щоб виконати тести, можна просто вказати браузер на маршрут /web/tests. У цьому випадку браузер завантажить усі ресурси, а QUnit візьме на себе роботу.
є певний код у qunit_config.js, який записує в консоль певну інформацію, коли тест проходить чи не проходить.
ми хочемо, щоб runbot також запускав ці тести, тому існує тест (у test_js.py), який просто породжує браузер і спрямовує його на URL-адресу web/tests. Зауважте, що метод browser_js створює безголовий екземпляр Chrome.
Модульність і тестування¶
Завдяки тому, як Odoo розроблено, будь-який аддон може змінювати поведінку інших частин системи. Наприклад, аддон voip може змінити віджет FieldPhone для використання додаткових функцій. Це не дуже добре з точки зору системи тестування, оскільки це означає, що тест у веб-доповненні буде невдалим щоразу, коли встановлено доповнення VoIP (зверніть увагу, що Runbot запускає тести з усіма встановленими додатками).
У той же час наша система тестування хороша, оскільки вона може виявити, коли інший модуль порушує певну основну функціональність. Повного вирішення цієї проблеми немає. Наразі ми вирішуємо це в кожному окремому випадку.
Зазвичай не варто змінювати якусь іншу поведінку. Для нашого прикладу VoIP, безперечно, чистіше додати новий віджет FieldVOIPPhone і змінити декілька представлень, які його потребують. Таким чином, віджет FieldPhone не зазнає впливу, і обидва можна перевірити.
Додавання нового тесту¶
Припустімо, що ми підтримуємо аддон my_addon і хочемо додати тест для деякого коду javascript (наприклад, якусь службову функцію myFunction, розташовану в my_addon.utils). Процес додавання нового тестового прикладу такий:
створити новий файл my_addon/static/tests/utils_tests.js. Цей файл містить базовий код для додавання модуля QUnit my_addon > utils.
odoo.define('my_addon.utils_tests', function (require) { "use strict"; var utils = require('my_addon.utils'); QUnit.module('my_addon', {}, function () { QUnit.module('utils'); }); });
У my_addon/assets.xml додайте файл до основних тестових ресурсів:
<?xml version="1.0" encoding="utf-8"?> <odoo> <template id="qunit_suite_tests" name="my addon tests" inherit_id="web.qunit_suite_tests"> <xpath expr="//script[last()]" position="after"> <script type="text/javascript" src="/my_addon/static/tests/utils_tests.js"/> </xpath> </template> </odoo>
Перезапустіть сервер і оновіть my_addon або зробіть це з інтерфейсу (щоб переконатися, що новий тестовий файл завантажено)
Додайте тестовий приклад після визначення набору підтестів utils:
QUnit.test("some test case that we want to test", function (assert) { assert.expect(1); var result = utils.myFunction(someArgument); assert.strictEqual(result, expectedResult); });
Відвідайте /web/tests/, щоб переконатися, що тест виконано
Допоміжні функції та спеціальні твердження¶
Без сторонньої допомоги досить важко протестувати деякі частини Odoo. Зокрема, представлення є складними, оскільки вони взаємодіють із сервером і можуть виконувати багато rpc, які потрібно імітувати. Ось чому ми розробили кілька спеціалізованих допоміжних функцій, розташованих у test_utils.js.
Функції макетного тестування: ці функції допомагають налаштувати тестове середовище. Найважливішим випадком використання є висміювання відповідей, наданих сервером Odoo. Ці функції використовують макетний сервер. Це клас javascript, який симулює відповіді на найпоширеніші методи моделі: read, search_read, nameget, …
Помічники DOM: корисні для симуляції подій/дій щодо певної цілі. Наприклад, testUtils.dom.click виконує клацання по цілі. Зауважте, що це безпечніше, ніж робити це вручну, оскільки воно також перевіряє, чи ціль існує та чи є видимою.
створити помічники: це, ймовірно, найважливіші функції, експортовані test_utils.js. Ці помічники корисні для створення віджетів із макетом середовища та великою кількістю дрібних деталей, щоб якомога більше імітувати реальні умови. Найважливішим, безумовно, є createView.
qunit assertions: QUnit можна розширити за допомогою спеціальних тверджень. Для Odoo ми часто тестуємо деякі властивості DOM. Ось чому ми зробили деякі твердження, щоб допомогти з цим. Наприклад, твердження containsOnce приймає widget/jQuery/HtmlElement і селектор, а потім перевіряє, чи ціль містить точно один збіг для селектора css.
Наприклад, за допомогою цих помічників ось як може виглядати простий тест форми:
QUnit.test('simple group rendering', function (assert) {
assert.expect(1);
var form = testUtils.createView({
View: FormView,
model: 'partner',
data: this.data,
arch: '<form string="Partners">' +
'<group>' +
'<field name="foo"/>' +
'</group>' +
'</form>',
res_id: 1,
});
assert.containsOnce(form, 'table.o_inner_group');
form.destroy();
});
Зверніть увагу на використання помічника testUtils.createView та твердження containsOnce. Крім того, контролер форми було належним чином знищено в кінці тесту.
Кращі практики¶
Без особливого порядку:
усі тестові файли слід додати в some_addon/static/tests/
для виправлень помилок переконайтеся, що тест не вдається без виправлення помилок і проходить разом із ним. Це гарантує, що він дійсно працює.
намагайтеся мати мінімальну кількість коду, необхідного для роботи тесту.
зазвичай два невеликих тести краще, ніж один великий. Менший тест легше зрозуміти та виправити.
завжди прибирання після тесту. Наприклад, якщо ваш тест створює екземпляр віджета, він має знищити його в кінці.
немає необхідності мати завершене і повне покриття коду. Але додавання кількох тестів дуже допомагає: це гарантує, що ваш код не зламано повністю, і щоразу, коли помилку виправлено, набагато простіше додати тест до існуючого набору тестів.
якщо ви хочете перевірити якесь негативне твердження (наприклад, що HtmlElement не має певного класу css), тоді спробуйте додати позитивне твердження в той самий тест (наприклад, виконавши дію, яка змінює стан). Це допоможе уникнути знищення тесту в майбутньому (наприклад, якщо буде змінено клас css).
Поради¶
запуск лише одного тесту: ви можете (тимчасово!) змінити визначення QUnit.test(…) на QUnit.only(…). Це корисно, щоб переконатися, що QUnit виконує лише цей конкретний тест.
прапор налагодження: більшість функцій утиліти create мають режим налагодження (активується параметром debug: true). У цьому випадку цільовий віджет буде розміщено в DOM замість прихованого специфічного фікстура qunit, і більше інформації буде зареєстровано. Наприклад, усі імітовані мережеві комунікації будуть доступні в консолі.
під час роботи над невдалим тестом зазвичай додають позначку налагодження, а потім коментують кінець тесту (зокрема, виклик знищення). Завдяки цьому можна безпосередньо бачити стан віджета, а ще краще – маніпулювати віджетом, натискаючи/взаємодіючи з ним.
Інтеграційне тестування¶
Тестування коду Python і коду JS окремо дуже корисно, але це не доводить, що веб-клієнт і сервер працюють разом. Щоб зробити це, ми можемо написати інший тип тесту: тури. Тур – це міні-сценарій якогось цікавого ділового потоку. Він пояснює послідовність кроків, яких слід виконати. Потім виконавець тесту створить браузер PhantomJs, направить його на відповідну URL-адресу та імітує клацання та введення відповідно до сценарію.
Написання тестового туру¶
Структура¶
Щоб написати тестовий тур для your_module
, почніть зі створення необхідних файлів:
your_module
├── ...
├── static
| └── src
| └── js
| └── tours
| └── your_tour.js
├── tests
| ├── __init__.py
| └── test_calling_the_tour.py
├── views
| └── your_module_views.xml
└── __manifest__.py
Тоді ви можете:
додати
your_tour.js
як актив уyour_module_views.xml
.оновіть
__manifest__.py
, щоб завантажитиyour_module_views.xml
.оновіть
__init__.py
в папціtests
, щоб імпортуватиtest_calling_the_tour
.
Перегляньте також
Javascript¶
Налаштуйте свій тур, зареєструвавши його.
odoo.define('your_module.your_tour_name', function (require) { 'use strict'; var tour = require('web_tour.tour'); tour.register('your_tour_name', { url: '/web', // Here, you can specify any other starting url test: true, }, [ // Your sequence of steps ]); });
Додайте будь-який крок, який хочете.
Кожен крок містить принаймні тригер. Ви можете скористатися попередньо визначеними кроками або написати свій власний персоналізований крок.
Ось кілька прикладів кроків:
Example
// First step
tour.stepUtils.showAppsMenuItem(),
// Second step
{
trigger: '.o_app[data-menu-xmlid="your_module.maybe_your_module_menu_root"]',
edition: 'community' // Optional
}, {
// Third step
},
Example
{
trigger: '.js_product:has(strong:contains(Chair floor protection)) .js_add',
extra_trigger: '.oe_advanced_configurator_modal', // This ensure we are in the wizard
},
Example
{
trigger: 'a:contains("Add a product")',
// Extra-trigger to make sure a line is added before trying to add another one
extra_trigger: '.o_field_many2one[name="product_template_id"] .o_external_button',
},
Ось кілька можливих аргументів для ваших індивідуальних кроків:
trigger: селектор/елемент/jQuery, який потрібно активувати
extra-trigger: додатковий селектор/елемент/jQuery, який має бути присутнім перед початком наступного кроку. Це особливо корисно, коли туру потрібно чекати, поки відкриється майстер, рядок, доданий до списку…
запуск: необов’язкова дія для запуску, за замовчуванням
клацнути
абоперевірити тест
, якщо ви запускаєте введення. Можлива безліч дій. Ось деякі з них:click
,dbclick
,tripleclick
,text Example
,drag_and_drop selector1 selector2
…edition: необов’язково,
Якщо ви не вкажете випуск, крок буде активним і в community, і в enterprise.
Іноді на enterprise чи в community крок буде іншим. Потім ви можете написати два кроки: один для enterprise версії та інший для community.
Як правило, ви хочете вказати випуск для кроків, які використовують головне меню, оскільки головні меню відрізняються в community та на enterprise.
edition: необов’язково
id: необов’язковий
auto: необов’язковий
in_modal: необов’язковий
Порада
Інструменти розробника вашого веб-переглядача - найкращий інструмент для пошуку елемента, який ваш тур має використовувати як тригер/додатковий тригер.
Перегляньте також
Python¶
Щоб розпочати огляд із тесту python, успадкуйте клас від HTTPCase
і викличте start_tour
:
def test_your_test(self):
# Optional Setup
self.start_tour("/web", 'your_module.your_tour_name', login="admin")
# Optional verifications
Поради щодо налагодження¶
Запуск туру в режимі налагодження¶
Спочатку активуйте режим розробника за допомогою ?debug=tests
. Потім відкрийте меню налагодження та натисніть Почати огляд. Тепер ви можете запустити свій тур звідти, натиснувши кнопку Тестувати
.
Ви також можете додати цей крок у свій тур, щоб зупинити його там, де ви хочете:
{
trigger: "body",
run: () => {debugger}
}
Застереження
Майте на увазі, що під час запуску огляду будь-які дані, додані до налаштувань вашого тесту python, не будуть присутні в огляді, якщо ви не запустили тест, викликаючи огляд із точкою зупину.
Скріншоти та скрінкасти під час тестування browser_js¶
Під час запуску тестів, які використовують HttpCase.browser_js з командного рядка, браузер Chrome використовується в безголовому режимі. За замовчуванням, якщо тест не вдається, знімок екрана у форматі PNG робиться в момент невдачі та записується в нього
'/tmp/odoo_tests/{db_name}/screenshots/'
Починаючи з Odoo 13.0, було додано два нові аргументи командного рядка для керування цією поведінкою: --screenshots
і --screencasts
Тестування продуктивності¶
Кількість запитів¶
Одним із способів перевірки продуктивності є вимірювання запитів до бази даних. Це можна перевірити вручну за допомогою параметра CLI --log-sql
. Якщо ви хочете встановити максимальну кількість запитів для операції, ви можете використовувати метод assertQueryCount()
, інтегрований у тестові класи Odoo.
with self.assertQueryCount(11):
do_something()
Заповнення бази даних¶
Odoo CLI пропонує функцію заповнення бази даних.
odoo-bin populate
Замість стомлюючої ручної або програмної специфікації тестових даних можна використовувати цю функцію, щоб заповнити базу даних на вимогу бажаною кількістю тестових даних. Це можна використовувати для виявлення різноманітних помилок або проблем із продуктивністю в перевірених потоках.
Щоб визначити цю функцію для даної моделі, можна визначити наступні методи та атрибути.
- Model._populate_sizes¶
Повертає dict, що відображає символічні розміри (
'small'
,'medium'
,'large'
) на цілі числа, надаючи мінімальну кількість записів, яку має створити_populate()
.Стандартні розміри сукупності:
small
: 10medium
: 100large
: 1000
- Model._populate_dependencies¶
Return the list of models which have to be populated before the current one.
- Тип повернення
- Model._populate(size)[source]¶
Створіть записи для заповнення цієї моделі.
- Параметри
size (str) – symbolic size for the number of records:
'small'
,'medium'
or'large'
- Model._populate_factories()[source]¶
Generates a factory for the different fields of the model.
factory
is a generator of values (dict of field values).Factory skeleton:
def generator(iterator, field_name, model_name): for counter, values in enumerate(iterator): # values.update(dict()) yield values
See
odoo.tools.populate
for population tools and applications.- Повертає
list of pairs(field_name, factory) where
factory
is a generator function.- Тип повернення
Примітка
It is the responsibility of the generator to handle the field_name correctly. The generator could generate values for multiple fields together. In this case, the field_name should be more a «field_group» (should be begin by a «_»), covering the different fields updated by the generator (e.g. «_address» for a generator updating multiple address fields).
Примітка
Ви повинні визначити принаймні _populate()
або _populate_factories()
у моделі, щоб увімкнути заповнення бази даних.
Example model¶
from odoo.tools import populate
class CustomModel(models.Model)
_inherit = "custom.some_model"
_populate_sizes = {"small": 100, "medium": 2000, "large": 10000}
_populate_dependencies = ["custom.some_other_model"]
def _populate_factories(self):
# Record ids of previously populated models are accessible in the registry
some_other_ids = self.env.registry.populated_models["custom.some_other_model"]
def get_some_field(values=None, random=None, **kwargs):
""" Choose a value for some_field depending on other fields values.
:param dict values:
:param random: seeded :class:`random.Random` object
"""
field_1 = values['field_1']
if field_1 in [value2, value3]:
return random.choice(some_field_values)
return False
return [
("field_1", populate.randomize([value1, value2, value3])),
("field_2", populate.randomize([value_a, value_b], [0.5, 0.5])),
("some_other_id", populate.randomize(some_other_ids)),
("some_field", populate.compute(get_some_field, seed="some_field")),
('active', populate.cartesian([True, False])),
]
def _populate(self, size):
records = super()._populate(size)
# If you want to update the generated records
# E.g setting the parent-child relationships
records.do_something()
return records
Інструменти наповнення¶
Для легкого створення необхідних генераторів даних доступно кілька інструментів для заповнення даних.
- odoo.tools.populate.cartesian(vals, weights=None, seed=False, formatter=<function format_str>, then=None)[source]¶
Return a factory for an iterator of values dicts that combines all
vals
for the field with the other field values in input.- Параметри
vals (list) – list in which a value will be chosen, depending on
weights
weights (list) – list of probabilistic weights
seed – optional initialization of the random number generator
formatter (function) – (val, counter, values) –> formatted_value
then (function) – if defined, factory used when vals has been consumed.
- Повертає
function of the form (iterator, field_name, model_name) -> values
- Тип повернення
- odoo.tools.populate.compute(function, seed=None)[source]¶
Return a factory for an iterator of values dicts that computes the field value as
function(values, counter, random)
, wherevalues
is the other field values,counter
is an integer, andrandom
is a pseudo-random number generator.
- odoo.tools.populate.constant(val, formatter=<function format_str>)[source]¶
Return a factory for an iterator of values dicts that sets the field to the given value in each input dict.
- odoo.tools.populate.iterate(vals, weights=None, seed=False, formatter=<function format_str>, then=None)[source]¶
Return a factory for an iterator of values dicts that picks a value among
vals
for each input. Once allvals
have been used once, resume asthen
or as arandomize
generator.- Параметри
vals (list) – list in which a value will be chosen, depending on
weights
weights (list) – list of probabilistic weights
seed – optional initialization of the random number generator
formatter (function) – (val, counter, values) –> formatted_value
then (function) – if defined, factory used when vals has been consumed.
- Повертає
function of the form (iterator, field_name, model_name) -> values
- Тип повернення
- odoo.tools.populate.randint(a, b, seed=None)[source]¶
Return a factory for an iterator of values dicts that sets the field to the random integer between a and b included in each input dict.
- odoo.tools.populate.randomize(vals, weights=None, seed=False, formatter=<function format_str>, counter_offset=0)[source]¶
Return a factory for an iterator of values dicts with pseudo-randomly chosen values (among
vals
) for a field.- Параметри
- Повертає
function of the form (iterator, field_name, model_name) -> values
- Тип повернення