TestMace
Search…
Script
Узел, выполняющий сценарии, написанные на JavaScript. Script будет полезен для решения разных задач:
  • Реализация сложных тестов над результатами одного или нескольких других узлов
  • Генерация тестовых данных
  • Преобразование переменных других узлов
  • Выполнение операций для приведения тестируемой системы в заданное состояние (set_up, tear_down)
  • Отладка и доступ к состоянию всех узлов проекта

Редактирование

Окно редактирования узла разделено на две области - окно редактирования кода и окно консольного вывода. Для скрытия консоли нажмите на кнопку
.
Над окном консольного вывода расположена панель инструментов для управления поведением консоли:
  • /
    - переключение между режимами сохранения результатов выполнения в консоли.
    - очистка консоли перед каждым запуском скрипта,
    - накопление результатов запуска.
  • - автоматическая прокрутка к последней строке вывода консоли
  • - очистить текущий вывод консоли

Запуск

Скрипт начинает выполнение при нажатии на кнопку RUN. Узел заканчивает свое выполнение после исполнения всех строки кода и после завершения всех асинхронных задач (например, setTimeout). Скрипт считается выполненным успешно при выполнении следующих условий:
  • В коде не выявлено синтаксических ошибок
  • При выполнении все выброшенные исключения обработаны
  • Выполнение заняло не более 30 секунд (по истечении этого времени скрипт будет прерван)
Вызов скрипта обернут в функцию, поэтому для того, чтобы прервать выполнение без ошибок воспользуйтесь инструкцией возврата: return;
Чтобы прервать выполнения скрипта с ошибкой, воспользуйтесь выбросом любого исключения: throw new Error('Something went wrong');

Библиотеки

Запуск осуществляется в виртуальном окружении node.js. Пользователю доступно некоторые модули из node.js, а также все встроенные возможности JavaScript, поддерживаемые движком V8.
Осуществляется поддержка стандарта ECMAScript 6

Доступные модули из node.js

  • fs - работа с файловой системой

Доступные сторонние модули

  • lodash - библиотека со множеством утилитарных алгоритмов
  • moment.js - библиотека для работы с датами
  • CryptoJS - библиотека реализующая множество криптографических алгоритмов
  • random-js - библиотека для генерации математически корректных случайных чисел
  • faker.js - библиотека для генерации случайных данных для свойств различных сущностей
  • chai.js - библиотека предоставляющая комфортные интерфейсы для проверки логических утверждений.
  • request - библиотека предоставляющая мощный HTTP-клиент.
  • axios - библиотека для отправки HTTP-запросов

Контекст выполнения

Ниже приведены объекты и функции глобальной области видимости скрипта.

Доступ к сторонним модулям

Все описанные модули автоматически подключаются к контексту выполнения и доступны в глобальной области видимости.

lodash

1
_.sum([1, 2, 3, 4]) // 10
Copied!

moment.js

1
const now = moment(new Date()).format();
Copied!

CryptoJS

1
const hash = crypto.MD5('Message');
Copied!

random-js

1
const randomEngine = new random.Random();
2
const shuffledArray = randomEngine.shuffle([1,2,3,4,5]);
Copied!

faker.js

1
const person = {
2
'name': faker.name.findName(),
3
'email': faker.internet.email()
4
};
Copied!

chai.js

1
const foo = 'bar';
2
3
// success
4
assert.equal(foo, 'bar');
5
expect(foo).to.equal('bar');
6
7
// failure
8
assert.equal(1, 0);
Copied!

request

Библиотека request не предоставляет интерфейса для работы с async/await. Одно из возможных решений это обернуть вызов request в объект Promise
1
await new Promise((resolve, reject) => {
2
request('https://docs-ru.testmace.com', (error, response, body) => {
3
try {
4
assert.equal(error, null);
5
assert.equal(response.statusCode, 206);
6
assert.notEqual(body, null);
7
resolve();
8
} catch(e) {
9
reject(e);
10
}
11
});
12
})
Copied!

axios

1
await axios.get('https://api.ipify.org?format=json')
Copied!

console.*

Методы для вывода данных в консоль: log, info, warn, error, debug, exception
Сигнатура методов совпадает с их стандартными версиями. Каждый тип события в консоли окрашивается в свой цвет. Каждая строка сопровождается указателем на строку и столбец, из которой произошел вызов функции вывода. События типа exception отображаются вместе со стеком вызовов внутри скрипта.

Асинхронный код

В предыдущих версиях TestMace пытался автоматически определить, когда скрипт завершал свою работу. В новых версиях эта возможность (вкупе с функцией finish()) удалена.
Скрипт может содержать асинхронные вызовы (например, Promise, setTimeout, addEventListener, колбэки при вызове встроенных модулей и пр.). Для работы с асинхронными операции JavaSсript предоставляет удобный механизм, называемый async/await. TestMace полностью поддерживает работу с async/await и полагается на него при выполнении асинхронных операций, чтобы определить, завершился ли скрипт или нет. К примеру, рассмотрим следующий код:
1
console.log('1');
2
setTimeout(() => console.log('2'), 100);
3
console.log('3');
Copied!
В консоли будет выведено
1
1
2
3
Copied!
Для того, чтобы исправить данное поведение, нужно явно указать, что данная операция асинхронная, используя ключевое слово await. Для удобства мы добавили функцию delay, которая позволяет поставить выполнение скрипта "на паузу" и совместима с async/await:
1
console.log('1');
2
await delay(100);
3
console.log('2');
4
console.log('3');
Copied!
В данном случае вывод будет:
1
1
2
2
3
3
Copied!
Для получения дальнейшей информации о механизме async/await рекомендуем обратиться к документации. Работу с async/await поддерживают следующие модули:
  • fs.promises
  • axios
В заключении отметим, что на работу скрипта отводится максимум 30 секунд, по истечению которых скрипт завершится с ошибкой.
В глобальной области видимости доступен объект для доступа к проекту и текущему Script узлу - tm.

tm

  • currentNode: nodeAPI - интерфейс текущего Script узла
  • project: nodeAPI - интерфейс узла проекта
  • env: envAPI - интерфейс для доступа к переменным окружения проекта
  • cookies: cookie[] - список установленных в проекте cookies
  • systemVars: object - объект, содержащий переменные окружения системы

nodeAPI

  • parent: nodeAPI - возвращает интерфейс для родительского узла. Для узла проекта значение будет null
  • name: string - имя данного узла
  • type: string - тип данного узла.
  • path: string - путь до данного узла, относительно корня проекта.
  • children: nodeAPI[] - список интерфейсов дочерних узлов
  • findChild(name: string): nodeAPI - поиск дочернего узла по его `name`. Если узел с таким именем не найдет, вернется null
  • next: nodeAPI - интерфейс следующего по порядку узла в группе. Если текущий узел является последним, то вернется null
  • prev: nodeAPI - интерфейс предыдущего по порядку узла в группе. Если текущий узел является первым в группе, то вернется null
  • nextNodes: nodeAPI[] - список всех узлов в группе следующих за текущим. Если текущий узел является последним, то вернется пустой список
  • prevNodes: nodeAPI[] - список всех узлов в группе предшествующих текущему. Если текущий узел является первым по порядку, то вернется пустой список
  • vars: object - объект, содержащий все статические переменные данного узла
  • dynamicVars: object - объект, содержащий все динамические переменные данного узла.
  • setDynamicVar(name: string, value: any): void - метод устанавливает динамическую переменную `name` cо значением `value` для данного узла.

requestNodeAPI

Узел типа RequestStep обладает расширенным интерфейсом.
  • request: object - объект содержит настройки запроса узла.
  • response: object - объект содержит результаты последнего выполнения запроса

envAPI

  • active: string - имя активного окружения
  • vars: object - объект содержит переменные текущего окружения

Примеры

Рекурсивный обход потомков узла

Данный пример демонстрирует как пробросить данные всем потомкам текущего узла.
1
const current = tm.currentNode;
2
const parent = current.parent;
3
if (!parent) {
4
console.warn(`Parent of ${current.path} not found`);
5
return;
6
}
7
8
const value = parent.vars['ID'];
9
if (!value) {
10
console.warn(`Node ${parent.path} hasn't have value for ID`);
11
return;
12
}
13
console.log(`Parent ID = ${value}`);
14
15
const setIDToNode = (node) => {
16
node.setDynamicVar('ID', value);
17
};
18
19
const traverseDescendants = (node, func, depth) => {
20
node.children.forEach((child) => {
21
func(node);
22
23
indent = '\t'.repeat(depth);
24
console.debug(
25
`${indent}${child.path}`,
26
`${indent}Value: ${child.dynamicVars['ID']}`
27
);
28
29
traverseDescendants(child, func, depth+1);
30
});
31
};
32
33
traverseDescendants(parent, setIDToNode, 0);
Copied!

Поиск узлов по имени

В данном примере демонстрируется как можно найти дочерний узел по его имени при помощи метода findChild
1
const current = tm.currentNode;
2
const scriptNode = current.parent.findChild(current.name);
3
assert.equal(current, scriptNode);
Copied!

Генерация и сохранение данных в переменные

В данном примере мы генерируем случайную строку-идентификатор при помощи библиотеки faker и устанавливаем ее следующему узлу как динамическую переменную UUID. После выполнения скрипта из следующего узла можно сослаться на данную переменную в URL, теле запроса и т.п.
1
const current = tm.currentNode;
2
const uuid = faker.random.uuid();
3
const anotherNode = current.next;
4
anotherNode.setDynamicVar('UUID', uuid);
Copied!

Файловое представление

1
{
2
"type": "object",
3
"properties": {
4
"type": {
5
"description": "Type of Script node",
6
"const": "Script",
7
"type": "string"
8
},
9
"script": {
10
"description": "Javascript code",
11
"type": "string"
12
},
13
"children": {
14
"description": "List of children names",
15
"type": "array",
16
"items": {
17
"type": "string"
18
},
19
"default": []
20
},
21
"variables": {
22
"$ref": "#/definitions/NodeVariables",
23
"description": "Node variables dictionary"
24
},
25
"name": {
26
"description": "Node name",
27
"type": "string"
28
}
29
},
30
"required": [
31
"children",
32
"name",
33
"script",
34
"type",
35
"variables"
36
],
37
"definitions": {
38
"NodeVariables": {
39
"type": "object",
40
"additionalProperties": {
41
"type": "string"
42
}
43
}
44
},
45
"$schema": "http://json-schema.org/draft-07/schema#"
46
}
Copied!
Last modified 1yr ago