Pull to refresh
0
InterSystems
InterSystems IRIS: СУБД, ESB, BI, Healthcare

Юнит-тесты в Caché – это просто

Reading time 5 min
Views 8K
Больше всего программисты любят программы, в которых не нужно исправлять баги. Шагом на пути к этой несбыточной мечте является написание юнит-тестов. В Caché, как и в любой современной СУБД, есть реализация фреймворка для автоматического выполнения тестов.



За подробным описанием философии и методологии написания юнит-тестов я вас отсылаю в интернет – на Хабр и к замечательной книге «Growing Object-Oriented Software Guided by Tests». На русский язык она не переведена, но английский там простой и понятный.

В качестве примера напишем тщательно оттестированный метод, который принимает на входе фамилию, имя и должность, сохраняет их в таблице и возвращает id созданного объекта. Таблица будет называться Tutorial.Person.

По всем канонам TDD сначала создадим тест:

Class Tutorial.Test.Person Extends %UnitTest.TestCase
{
Method TestNewPerson() {
    
set surname "John"
    
set name "Doe"
    
set job "quality assurance developer"
    
set id ##class(Tutorial.Person).AddPerson(surnamenamejob, .ec)
    
do $$$AssertStatusOK(ec)
    
    
set ##class(Tutorial.Person).%OpenId(id)
    
do $$$AssertTrue($IsObject(p))
    
    
do $$$AssertEquals(p.Surnamesurname)
    
do $$$AssertEquals(p.Namename)
    
do $$$AssertEquals(p.Jobjob)
}
}

Класс, содержащий тесты, должен наследовать от класса %UnitTest.TestCase. Все методы этого класса, которые начинаются со слова Test, считаются тестами. Макросы и методы, доступные наследникам %UnitTest.TestCase, описаны в документации.

Тесты запускает метод ##class(%UnitTest.Manager).RunTest(subdir, spec). Этот метод загружает и выполняет все классы с тестами из подпапки subdir каталога, который указан в глобале ^UnitTestRoot. Spec — дополнительные ключи для запуска. По умолчанию после выполнения классы с тестами удаляются. Мы этого, конечно, не допустим, добавив вторым аргументом к RunTest строку с ключом «/nodelete».

Итак, создайте папку c:\unittests\habr и экспортируйте в неё наш класс с тестом (понятно, что папку вы можете назвать как угодно и что подпапку лучше бы было назвать tutorial, обозначая пакет, который мы будет проверять; но, чтобы подчеркнуть, что эти названия не обязаны совпадать, я назвал подпапку habr). Экспортируйте тест в эту папку (я назвал файл tutorial.test.person.xml) и вперёд в терминал!

USER>set ^UnitTestRoot="c:\UnitTests"
 
USER>do ##class(%UnitTest.Manager).RunTest("habr","/nodelete/noload")
 
==================================================================
Directory: C:\UnitTests\habr\
==================================================================
  habr begins ...
Перечислить стартовавшие элементы в каталоге 07/26/2015 00:59:23 '*.xml;*.XML'
 
Вывести файл C:\UnitTests\habr\tutorial.test.person.xml в xml
Вывод завершен успешно.
 
    Tutorial.Test.Person begins ...
      TestNewPerson() begins ...
LogStateStatus:0:TestNewPerson:ОШИБКА #5002: Ошибка: <CLASS DOES NOT EXIST>zTestNewPerson+4^Tutorial.Test.Person.1 *Tutorial.Person  <<==== **FAILED**
habr:Tutorial.Test.Person:TestNewPerson:
      TestNewPerson failed
    Tutorial.Test.Person failed
  Skipping deleting classes
  habr failed
 
Use the following URL to view the result:
http://192.168.1.6:57772/csp/sys/%25UnitTest.Portal.Indices.cls?Index=2&$NAMESPACE=USER
Some tests FAILED in suites:
  habr

Обратите внимание — я указал два ключа в методе RunTest: /nodelete — не удалять класс с тестами после запуска, /noload — не загружать и не компилировать сам класс.

Как видите, наш тест благополучно завершился с ошибкой. В конце вывода напечатан URL для веб-интерфейса результатов запуска этого теста и истории запусков. Обратите внимание, что этот URL ссылается на веб-приложение /csp/sys, указывая нужную область аргументом. Если вы в Портале управления смените область на USER и потом перейдёте по ссылкам Обозреватель системы > Инструменты > Портал UnitTest, то, вероятно, вы увидите ошибку Forbidden при попытке открыть следующий URL:

http://<yourhost>:<yourport>/csp/user/%25UnitTest.Portal.Home.zen?$NAMESPACE=USER

Эта ошибка вызвана тем, что в Caché по умолчанию запрещено обращение к системным (процентным) страницам из веб-приложений не начинающихся на /csp/sys. Чтобы разрешить открытие страниц, относящихся к юнит-тестам в области USER, выполните следующую команду в терминале в области %SYS:

%SYS>Set ^SYS("Security","CSP","AllowPrefix","/csp/user/","%UnitTest.")=1

Так как классов с тестами почти всегда много, я создаю отдельный класс с методом, который сначала выгружает все классы с тестами в каталог, а затем вызывает RunTest:

Class Tutorial.Test.All Abstract ]
{
ClassMethod runall()
{
    
do $system.OBJ.Export("Tutorial.Test.Person.cls", ^UnitTestRoot_"\habr\"_"tutorial.test.person.xml")
    
; здесь можно добавлять экспорт новых классов с тестами.
    
    
do ##class(%UnitTest.Manager).RunTest("habr","/nodelete/noload")
}
}

Последнее, что осталось, это создать класс Tutorial.Person и метод AddPerson, запустить тесты и увидеть долгожданное «ALL PASSED»:

USER>do ##class(Tutorial.Test.All).runall()
 
Экспорт  в XML начался в 07/26/2015 01:25:12
Экспортируемый класс: Tutorial.Test.Person
Экспорт успешно завершен.
 
==================================================================
Directory: C:\UnitTests\habr\
==================================================================
  habr begins ...
Перечислить стартовавшие элементы в каталоге 07/26/2015 01:25:12 '*.xml;*.XML'
 
Вывести файл C:\UnitTests\habr\tutorial.test.person.xml в xml
Вывод завершен успешно.
 
    Tutorial.Test.Person begins ...
      TestNewPerson() begins ...
        AssertStatusOK:ec (passed)
        AssertTrue:$IsObject(p) (passed)
        AssertEquals:p.Surname== surname (passed)
        AssertEquals:p.Name== name (passed)
        AssertEquals:p.Job== job (passed)
        LogMessage:Duration of execution: .000503 sec.
      TestNewPerson passed
    Tutorial.Test.Person passed
  Skipping deleting classes
  habr passed
 
Use the following URL to view the result:
http://192.168.1.6:57772/csp/sys/%25UnitTest.Portal.Indices.cls?Index=7&$NAMESPACE=USER
All PASSED

На этом месте пытливый читатель (привет, tsafin) возмутится: «Зачем столько извращений, чтобы запустить простой тест?! Нельзя ли как-нибудь попроще?». Конечно нельзя можно! Но не сильно проще. Если папка c:\UnitTests\habr есть и в ^UnitTestRoot прописан путь «c:\UnitTests», то отдельно взятый класс с тестами можно (без предварительной выгрузки) запустить так:

do ##class(%UnitTest.Manager).DebugRunTestCase("habr","Tutorial.Test.Person")

Не забывайте, Caché — это СУБД. Все результаты тестов сохраняются и доступны для SQL-запросов. Откройте окно Портала для ввода SQL. Поставьте галочку рядом со словом Система, чтобы показать системные классы. Выберите в выпадающем списке схему %UnitTest_Result. Например, в столбце Duration некоторых таблиц этой схемы хранится продолжительность отдельного теста (или метода внутри теста). Запуская тесты регулярно, вы можете следить не только за тем, чтобы они не падали, но и чтобы время их выполнения не увеличивалось.

В заключение рекомендую пройти небольшой туториал по юнит-тестам, который есть в документации по Caché, а также просмотреть описание классов пакета %UnitTest в справочнике классов.
Tags:
Hubs:
+18
Comments 9
Comments Comments 9

Articles

Information

Website
www.intersystems.com
Registered
Founded
1978
Employees
1,001–5,000 employees
Location
США