Автоматическое тестирование на PL/SQL


Зачем нужно автоматическое тестирование


Цели и задачи автотестирования

  1. Обеспечить автоматизацию тестирования, которое в силу своей сложности зачастую вовсе не проводится. Необходимо упростить задачу тестирования функциональности, устранить нудную деятельность по осуществлению одного и того же сценария посредством пользовательского интерфейса, как следствие, снизить влияние человеческого фактора в тестировании.
  2. Обеспечить постоянный регрессионный контроль над функциональностью, повысить качество создаваемых систем и вносимых в систему изменений. Запускать проверку автотестов каждый вечер, по окончании рабочего дня.
  3. Обособить и перенести на сервер реализацию бизнес-процессов, что позволит отделить пользовательский интерфейс от реализации, упростит внесение изменений в код и интеграцию изменений в "боевые" релизы.
  4. Упростить понимание в реализации требуемой бизнес-логики посредством написания алгоритмов и интерфейсов через автотест, и как следствие, повысить качество создаваемых алгоритмов и интерфейсов. Обеспечить безопасный рефакторинг существующего кода.
  5. Закрепить сведения о корректной функциональности, то есть варианты использования, в коде автотеста.

О пользе применения автотестов

  1. При помощи автотестирования ликвидируются случаи регрессии функциональности.
  2. Автотест принуждает перенести реализацию бизнес-логики на сервер, что само по себе чрезвычайно полезно и удобно как с точки зрения внедрения и сопровождения, так и с точки зрения оптимизации.
  3. Автотест ликвидирует страх, связанный с внесением в работающую систему изменений функциональности - у нас есть тест, он скажет где, что и когда "поехало". Не нужно проводить длительные исследования на предмет того, какой участок кода от чего зависит. Не нужно требовать от сотрудников сверхаккуратности.
  4. Автотест - это ответ на вопрос "как должен работать этот алгоритм?", который задается любым разработчиком при реализации этого алгоритма, то есть автотест позволяет разработчику лучше разобраться в том, что от него требуется. Это итерационный процесс: есть входные данные, есть выходные, используя заглушки, постепенно вырисовывается интерфейс к алгоритму, которые постепенно реализуется, а тест всегда дает ответ на вопрос "правильно ли реализован алгоритм".
  5. Автотест представляет собой отличную отладочную среду - вносим в бизнес-логику точки наблюдения за переменными и запускаем тест, ответ отыскивается моментально, не нужно посредством пользовательского интерфейса нудно проводить одну и ту же операцию по формированию входных параметров, не нужно подбирать отдельные идентификаторы для проверки процедур.

Термины

  1. Автотест - обособленный модуль, например, реализованный на PL/SQL, удовлетворяющий определенной сигнатуре, позволяющей запускать его на выполнение в автоматическом режиме, содержащий в себе вызовы методов бизнес-логики и логику проверки предусловий и постусловий этих методов.
  2. Предусловия метода - предикаты, которые должны принимать истинные значения перед выполнением кода метода (проверка на верные входные параметры). Проверка предусловий осуществляется внутри метода. Проверка предусловий метода в автотесте осуществляется путем передачи параметров, неверных с точки зрения алгоритма метода.
  3. Постусловия метода - предикаты, которые должны принимать истинные значения после выполнения метода (проверка на то, что метод отработал правильно). Проверка постусловия в автотесте осуществляется после вызова метода путем вычисления соответствующего предиката.


Использование механизма автотестирования


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

При разработке новой функциональности или кардинальной переработке существующей автотест пишется первым. По ходу разработки автотеста формируются требования к программному интерфейсу функциональности. Программный интерфейс реализуется в виде заглушек, а затем постепенно приобретает вид законченной функциональности.

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

Модуль автотеста реализуется в виде PL/SQL-пакета со строго определенной сигнатурой:

  1. Пакет располагается в схеме AUTOTEST.
  2. В начале тела пакета расположен комментарий, описывающий назначение теста и содержащий информацию о разработчике и дате разработке автотеста (см. Приложение). Необходимо максимально полно (детальон) описывать назначение теста, так как по комментарию определяется покрываемая тестом функциональность.
  3. В пакете определены три процедуры, таким образом, чтобы их можно было вызывать извне.
    • Initialize - подготовка к выполнению теста, например, вход в систему под нужным пользователем.
    • RunTest - логика автотеста.
    • Finalize - реализуется при необходимости, предназначен для удаления каких-либо данных после выполнения теста.

Логика автотеста должна содержать следующие проверки:

  1. Проверка на генерацию сообщений о недопустимых параметрах (проверка предусловий) - перехват исключений Oracle, генерируемых бизнес-логикой при передачи некорректных параметров или отклонении от задуманного хода выполнения алгоритма.
  2. Проверка истинности утверждения о результате работы функции (проверка постусловий) - проверка, что состояние системы переведено в требуемое (ожидаемое с точки зрения бизнес-процесса) состояние, например, экземпляр объекта создан, то есть идентификатор записи > 0.


Описание архитектуры


  1. AUTOTEST.LOG - пакет, реализующий поддержку протоколирования сообщений по ходу выполнения автотеста. Содержит низкоуровневые процедуры записи и чтения протокола, формируемого по ходу выполнения теста. Используется пакетами AUTOTEST.PLSQLTEST и AUTOTEST.FACTORY
  2. AUTOTEST.FACTORY - пакет, реализующий фабрику экземпляров автотестов и вспомогательные процедуры для получения метаданных пакетов автотестов.
    • GetAutoTestPackages - возвращает список доступных к исполнению автотестов, список оформлен в виде XML.
    • RunTest - запуск автотеста по полному имени пакета (включая схему).
  3. AUTOTEST.PLSQLTEST - пакет, реализующий базовый класс всех автотестов и процедуры проверки предусловий и постусловий.
    • AssertTrue - проверка постусловия на истинность.
    • AssertFalse - проверка постусловия на ложность.
    • AssertFailure - указание, что тест не сработал.
    • AssertException - вывод в протокол стек вызовов, приведших к генерации исключения.
    • AssertExcpectedException - проверка предусловий метода, перехват ожидаемого исключения.
    • AssertComment - вывод в протокол комментария о ходе выполнения теста.

Пример запуска и результата выполнения автотеста

Ниже приведен пример автотеста, который устанавливается вместе с остальными пакетами инфраструктуры. Тест запускается, после чего приводится результат выполнения автотеста, расположенный в буфере DBMS Output.


 
 ---------------------- исходный автотест --------------------------
 ...
 function Multiply( a_num number, a_mult number ) return number
 is
 begin
 	return a_num * a_mult;
 end;
 
 function LnFunction( a_num number ) return number
 is
 begin
 	if a_num = 0 then raise NO_DATA_FOUND; end if;
 	return ln( a_num );
 end;
   
 procedure RunTest
 is
 	l_temp number;
 begin
 	autotest.plsqltest.AssertTrue( 5 * 3 = multiply( 5, 3), 'Проверка умножения'); 
 	  
 	begin
 		autotest.plsqltest.AssertComment( 'Проверяем реакцию функции Ln на недопустимый параметр' );
 		
 		l_temp := LnFunction( 0 );
 		autotest.plsqltest.AssertTrue( false, 'Функция Ln проглотила недопустимый параметр' );
 		   
 	exception when NO_DATA_FOUND then
 		autotest.plsqltest.AssertExpectedException( sqlcode );
 	end;
 end;
 


 
 ---------------------- запуск автотеста ---------------------------
 declare
 begin
 	autotest.factory.runtestdbmsoutput('autotest.example');
 	rollback;
 end;
 


 
 ------------------ результат в dbms output ------------------------
 
 Ok: Проверка умножения
 Проверяем реакцию функции Ln на недопустимый параметр: 
 Ok: Сгенерировано ожидаемое исключение: ORA-01403: no data found
 Время выполнения: .01 c.
 Тест выполнился успешно
 


Дополнительные возможности


Запуск автотестов при помощи SQL-сценария не позволяет удобным образом организовать автоматизированный механизм запуска тестов каждую ночь и доставки почтового уведомления на соответствующий список рассылки. Конечно, можно использовать инструментарий Oracle: Jobs (задания), собственные пакеты по рассылке почтовых уведомлений. Однако, для быстрого старта предлагаю использовать возможности скриптовых компонентов и, например, страниц ASP.

Для организации доступа к механизму автотестирования нам потребуется скриптовый компонент, который позволит: получить список тестов в XML-формате, запустить тест и получить протокол его выполнения.

Скриптовый компонент для запуска автотеста

Ниже приведен программный интерфейс скриптового компонента plsqlUnitScript, позволяющего запускать автотесты, например с ASP-страницы.

ProgId: Autotest.PLSQLUnit

setDb - задание имени базы данных с автотестами

getTests - возвращает список доступных автотестов в XML-виде

executeTest - запускает тест на выполнение и возвращает объект автотеста. У объекта автотеста используется метод GetLog() для получения протокола о ходе выполнения теста

Запуск автотеста с внутреннего Web-сайта

Очень удобно было бы организовать запуск автотестов с внутреннего Web-сайта, таким образом, любой разработчик сможет запустить тест, не вникая в детали работы инфраструктуры. Ниже приводится пример ASP-страницы plsqlunit.asp, на которой реализован запуск автотеста посредством скриптового компонента Autotest.PLSQLUnit.


 
 <% Language=VBScript %>
 <%
  Response.AddHeader "Content-Type", "text/html; charset=windows-1251"
 
  ' создаем объект автотестирования
  Set t = CreateObject("Autotest.PLSQLUnit")
  ' укажем базу данных, на которой выполняются автотесты
  t.setDB "CORP9"
 
  ' отображаем список тестов на странице
  Set xml = CreateObject("Msxml2.DOMDocument")
  xml.loadXML( t.getTests("AUTOTEST") )
  Set tests = xml.selectNodes("tests/test")
  
  for i = 0 to tests.length - 1
  	Set package = tests.item(i).selectSingleNode("header/class")
  	response.write "<a href='plsqlunit.asp?test="& package.text &"'>"& package.text &"</a>"
  next
  
  If Request("test") <> "" Then
  	Response.Write "<div> </div><div>Результат выполнения автотеста "& Request("test") &": </div><hr>"
 	
  	Set test = t.executeTest( Request("test") )
  	xml.loadXML( test.GetLog() )
 	
 	Set lines = xml.selectNodes("log/line")
 	for i = 0 to lines.length - 1
 		Response.Write "<div>"& lines.item(i).text & "</div>"
 	next
 	
 	' отображаем время выполнения теста
 	Set executiontime = xml.selectSingleNode("log/executiontime")
 	If IsObject(executiontime) Then
 		Response.Write "<div>Время выполнения: "& executiontime.text &" с.</div>"
 	End If
 
 	' отображаем результат
 	Set result = xml.selectSingleNode("log/result")
 	If IsObject(result) Then
 		If result.text = "1" Then
 			resultText = "тест выполнен успешно"
 		Else
 			resultText = "тест не сработал"
 		End If
 		Response.Write "<hr><div>Результат: "& resultText &"</div>"
 	End If
  End If
 %>
 

Результат запуска теста с ASP-страницы

AUTOTEST.EXAMPLE

Результат выполнения автотеста AUTOTEST.EXAMPLE:

Ok:Проверка умножения

Проверяем реакцию функции Ln на недопустимый параметр:null

Ok:Сгенерировано ожидаемое исключение: ORA-01403: no data found

Время выполнения: .01 с.

Результат: тест выполнен успешно


Обсуждения