TestComplete: автоматический перезапуск упавших тестов

Иногда при запуске многих тестов (например, когда тесты прогоняются по ночам без участия человека) часть тестов могут упасть с ошибками, которые затем не воспроизводятся при повторном запуске. Причины этого могут быть разными, однако обычно хочется заранее знать, воспроизведётся ли проблема, если перезапустить тест?

Один из способов получить ответ – сделать автоматический перезапуск упавших тестов после того, как все тесты выполнились. В этой статье мы рассмотрим, как это можно сделать.

Для начала определимся, как будет происходить процесс запуска:

  1. основной запуск Project Suite будет производиться из bat-файла.
  2. во время работы тестов будет обновляться ещё один bat-файл, в котором будет прописываться запуск каждого упавшего теста по отдельности.
  3. по окончании работы тестов, запущенных в первом bat-файле, проверяем, существует ли второй bat-файл. Если он существует, значит есть упавшие тесты и файл необходимо запустить.

Сначала напишем несколько тестов:

function test1()
{
  Log.Message('Test 1');
}

function test2()
{
  Log.Message('Test 2');
}
function test3()
{
  Log.Error('Test 3');
}

function test4()
{
  Log.Message('Test 4');
}

function test5()
{
  Log.Error('Test 5');
}

Как видно из кода, у нас будет 3 теста без ошибок и 2 теста с ошибками. Все эти тесты нужно оформить в виде Test Items (правый клик на имени проекта, Edit | Test Items):

Теперь создадим обработчик ошибки OnLogError, который будет вызываться при каждом возникновении ошибки в тестах:

FAILED_TESTS_FILE = 'd:\\runFailedTests.bat';
TC_PATH = '"C:\\Program Files (x86)\\SmartBear\\TestComplete 10\\Bin\\TestComplete.exe"';
PJS_PATH = '"C:\\Users\\genka\\Documents\\TestComplete 10 Projects\\ProjectSuite1\\ProjectSuite1.pjs"';
TC_PARAMS = '/r /e /ns /SilentMode';

function GeneralEvents_OnLogError(Sender, LogParams)
{
  if(!aqFileSystem.Exists(FAILED_TESTS_FILE))
  {
    aqFile.Create(FAILED_TESTS_FILE);
  }

  if(Project.TestItems.Current != undefined)
  {
    var projectName = aqFileSystem.GetFileNameWithoutExtension(Project.FileName);
    var unitName = Project.TestItems.Current.ElementToBeRun.Caption.split('\\')[1].split(' - ')[0];
    var routineName = Project.TestItems.Current.ElementToBeRun.Caption.split('\\')[1].split(' - ')[1];
    var strToAdd = TC_PATH + ' ' + PJS_PATH + ' /project:' + projectName + ' /unit:' + unitName + ' /routine:' + routineName + ' ' + TC_PARAMS + '\n';
    aqFile.WriteToTextFile(FAILED_TESTS_FILE, strToAdd, aqFile.ctANSI);
  }
  else
  {
    Log.Message('Skipping adding test to failed list, test item is undefined');
  }
}

Сначала мы создаём bat-файл, если он ещё не создан (это произойдёт при первом срабатывании обработчика ошибок). Затем получаем имя проекта, модуля и функции, которые будут использоваться в командной строке для перезапуска тестов. Обратите внимание на условие:

if(Project.TestItems.Current != undefined)

Генерировать список упавших тестов нужно только при первом запуске. Когда мы запускаем тесты в первый раз, они запускаются с использованием Test Items, а значит свойство Project.TestItems.Current будет доступно, именно из него мы получаем затем имя модуля и функции. При повторном запуске мы передаём в командной строке имя функции, которую нужно запускать, а значит свойство Project.TestItems.Current будет undefined. В этом случае мы не обновляем bat-файл с упавшими тестами, а только кидаем в лог сообщение.

Теперь нам осталось создать первый bat-файл runAllTests.bat, с которого и начнётся работа тестов:

if exist D:\runFailedTests.bat (del D:\runFailedTests.bat)
"C:\Program Files (x86)\SmartBear\TestComplete 10\Bin\TestComplete.exe" "C:\Users\genka\Documents\TestComplete 10 Projects\ProjectSuite1\ProjectSuite1.pjs" /r /e /ns /SilentMode
if exist D:\runFailedTests.bat (D:\runFailedTests.bat)

Сначала мы удаляем старый файл с упавшими тестами, если он существует (он мог остаться с предыдущего запуска), затем выполняем основной Project Suite, после чего запускаем файл runFailedTests.bat, если он существует.

Если теперь запустить файл runAllTests.bat, то у нас сначала запустятся все 5 тестов, во время их работы сгенерируется файл runFailedTests.bat, который будет запущен по окончании работы первого тестов, когда закроется TestComplete.

И напоследок приведём текст сгенерированного файла runFailedTests.bat:

"C:\Program Files (x86)\SmartBear\TestComplete 10\Bin\TestComplete.exe" "C:\Users\genka\Documents\TestComplete 10 Projects\ProjectSuite1\ProjectSuite1.pjs" /project:TestRestartFailedTests /unit:Unit1 /routine:test3 /r /e /ns /SilentMode
"C:\Program Files (x86)\SmartBear\TestComplete 10\Bin\TestComplete.exe" "C:\Users\genka\Documents\TestComplete 10 Projects\ProjectSuite1\ProjectSuite1.pjs" /project:TestRestartFailedTests /unit:Unit1 /routine:test5 /r /e /ns /SilentMode

На этом основная часть закончена, однако можно немного улучшить это решение несколькими мелочами:

  • в блоке else обработчика ошибок можно генерировать ещё один список упавших тестов, которые отработали с ошибками при повторном запуске;
  • путь к проекту в нашем случае захардкоджен в переменной PJS_PATH, однако в реальном проекте его нужно получать из свойства Project.FileName, так как проектов обычно несколько;
  • пути к TestComplete.exe и к проекту в bat-файлах лучше оформить в виде переменных окружения, чтобы избавиться от длинных строк.