Script

Узел, выполняющий сценарии, написанные на JavaScript. Script будет полезен для решения разных задач:

  • Реализация сложных тестов над результатами одного или нескольких других узлов

  • Генерация тестовых данных

  • Преобразование переменных других узлов

  • Выполнение операций для приведения тестируемой системы в заданное состояние (set_up, tear_down)

  • Отладка и доступ к состоянию всех узлов проекта

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

Окно редактирования узла разделено на две области - окно редактирования кода и окно консольного вывода. Для скрытия консоли нажмите на кнопку .

Над окном консольного вывода расположена панель инструментов для управления поведением консоли:

  • / - переключение между режимами сохранения результатов выполнения в консоли. - очистка консоли перед каждым запуском скрипта, - накопление результатов запуска.

  • - автоматическая прокрутка к последней строке вывода консоли

  • - очистить текущий вывод консоли

Запуск

Скрипт начинает выполнение при нажатии на кнопку RUN. Узел заканчивает свое выполнение после исполнения всех строки кода и после завершения всех асинхронных задач (например, setTimeout). Скрипт считается выполненным успешно при выполнении следующих условий:

  • В коде не выявлено синтаксических ошибок

  • При выполнении все выброшенные исключения обработаны

  • Выполнение заняло не более 30 секунд (по истечении этого времени скрипт будет прерван)

Библиотеки

Запуск осуществляется в виртуальном окружении 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

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

moment.js

const now = moment(new Date()).format();

CryptoJS

const hash = crypto.MD5('Message');

random-js

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

faker.js

const person = { 
    'name': faker.name.findName(),
    'email': faker.internet.email()
};

chai.js

const foo = 'bar';

// success
assert.equal(foo, 'bar');
expect(foo).to.equal('bar');

// failure
assert.equal(1, 0);

request

Библиотека request не предоставляет интерфейса для работы с async/await. Одно из возможных решений это обернуть вызов request в объект Promise

await new Promise((resolve, reject) => {
  request('https://docs-ru.testmace.com', (error, response, body) => {
    try {
      assert.equal(error, null);
      assert.equal(response.statusCode, 206);
      assert.notEqual(body, null);
      resolve();
    } catch(e) {
      reject(e);
    }
  });
})

axios

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

console.*

Методы для вывода данных в консоль: log, info, warn, error, debug, exception

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

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

Скрипт может содержать асинхронные вызовы (например, Promise, setTimeout, addEventListener, колбэки при вызове встроенных модулей и пр.). Для работы с асинхронными операции JavaSсript предоставляет удобный механизм, называемый async/await. TestMace полностью поддерживает работу с async/await и полагается на него при выполнении асинхронных операций, чтобы определить, завершился ли скрипт или нет. К примеру, рассмотрим следующий код:

console.log('1');
setTimeout(() => console.log('2'), 100);
console.log('3');

В консоли будет выведено

1
3

Для того, чтобы исправить данное поведение, нужно явно указать, что данная операция асинхронная, используя ключевое слово await. Для удобства мы добавили функцию delay, которая позволяет поставить выполнение скрипта "на паузу" и совместима с async/await:

console.log('1');
await delay(100);
console.log('2');
console.log('3');

В данном случае вывод будет:

1
2
3

Для получения дальнейшей информации о механизме 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 - объект содержит переменные текущего окружения

Примеры

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

Данный пример демонстрирует как пробросить данные всем потомкам текущего узла.

const current = tm.currentNode;
const parent = current.parent;
if (!parent) {
  console.warn(`Parent of ${current.path} not found`);
  return;
}

const value = parent.vars['ID'];
if (!value) {
  console.warn(`Node ${parent.path} hasn't have value for ID`);
  return;
}
console.log(`Parent ID = ${value}`);

const setIDToNode = (node) => {
  node.setDynamicVar('ID', value);
};

const traverseDescendants = (node, func, depth) => {
  node.children.forEach((child) => {
    func(node);
    
    indent = '\t'.repeat(depth);
    console.debug(
      `${indent}${child.path}`,
      `${indent}Value: ${child.dynamicVars['ID']}`
    );
    
    traverseDescendants(child, func, depth+1);
  });
};

traverseDescendants(parent, setIDToNode, 0);

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

В данном примере демонстрируется как можно найти дочерний узел по его имени при помощи метода findChild

const current = tm.currentNode;
const scriptNode = current.parent.findChild(current.name);
assert.equal(current, scriptNode);

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

В данном примере мы генерируем случайную строку-идентификатор при помощи библиотеки faker и устанавливаем ее следующему узлу как динамическую переменную UUID. После выполнения скрипта из следующего узла можно сослаться на данную переменную в URL, теле запроса и т.п.

const current = tm.currentNode;
const uuid = faker.random.uuid();
const anotherNode = current.next;
anotherNode.setDynamicVar('UUID', uuid);

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

{
  "type": "object",
  "properties": {
    "type": {
      "description": "Type of Script node",
      "const": "Script",
      "type": "string"
    },
    "script": {
      "description": "Javascript code",
      "type": "string"
    },
    "children": {
      "description": "List of children names",
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "variables": {
      "$ref": "#/definitions/NodeVariables",
      "description": "Node variables dictionary"
    },
    "name": {
      "description": "Node name",
      "type": "string"
    }
  },
  "required": [
    "children",
    "name",
    "script",
    "type",
    "variables"
  ],
  "definitions": {
    "NodeVariables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}

Last updated

Was this helpful?