Автор: Максим Уваров Сейчас очень актуальна проблема тестирования. Все больше и больше фирм работают по схеме, так называемого, экстремального программирования. Важной основой данного решения является тестирование. Результаты тестирования очень важны, но как сделать их более эффективными? Об этом и пойдет речь в этой статье.
Предполагается, что у вас есть набор тестов, которые представляют собой программу, выводящую слово PASS, если тест прошел или слово FAIL, если тест не прошел. Приведем пример теста, который выполняется: #!/bin/bash echo "Simple test" echo "1" echo "2" echo "3" echo "PASS: LTT-05 test passed" Или теста которые не выполняется: #!/bin/bash echo "Simple test"\ echo "1" echo "2" echo "3" echo "FAIL: LTT-06 test failed" Для начала нам потребуется где-нибудь хранить статистику о поведенных тестах. Воспользуемся SQL базой данных mysql. База будет содержать примерно следующий вид: 1. время проведения теста 2. название тестируемой программы 3. номер теста 4. статус PASS - если тест прошел. и FAIL - если тест не прошел. 5. релиз статус При разборе данной таблицы можно вывести следующие результаты: 1. Время, занимаемое тестированием данной программы. 2. Определить трудные тесты, которые чаще всего не проходят. 3. Определить вероятность прохождения тестов. 4. Определить скорость запуска тестов (количество пройденных или не пройденных тестов, например за день). Сменим пароль для пользователя базы mysqladmin -u root password 'aa' Создадим базу следующим образом: mysqladmin -p create rtest (если нужно удалить базу с текущим именем, можно воспользоваться командой mysqladmin -p drop >rtest) Присоединимся к базе mysql -u root -p rtest Создадим пользователя, который будет работать с базой mysql> INSERT INTO user (Host, User, Password, Select_priv, Insert_priv, Update_priv, Delete_priv) VALUES ('%', 'bob', password('mypass'), 'Y', 'Y', 'Y', 'Y') Создадим таблицу: mysql> CREATE TABLE qastat( time CHAR(50) NOT NULL, testname CHAR(50) NOT NULL, testnum CHAR(10) NOT NULL, status ENUM('PASS', 'FAIL'), realise INT); Если realise = 1, то тестируется релизный продукт. Добавлять данные в таблицу будем следующим образом: INSERT INTO qastat( time, testname, testnum, status, realise) VALUES ('c_time', 'c_name', 'c_num', 'c_status', 'c_realise') Для считывания данных, используем следующий синтаксис: SELECT testname, testnum, status FROM qastat Теперь с помощью Python или любого другого языка, но в данном случае я использую именно Python, мы будем заполнять данную таблицу. Делается это так: import MySQLdb; def sqlinsert(time, testname, testnum, status, realise): query = 'INSERT INTO qastat(time, testname, testnum, status, realise) VALUES (\'' + time + '\', \'' \+ testname + '\', \'' + testnum + '\', \'' + status + '\', \'' + realise + '\')'; print query; mysql = MySQLdb.Connection('localhost', 'root', 'aa', 'rtest'); cursor = mysql.cursor(); cursor.execute(query); mysql.close(); return Соединяемся с базой через модуль MySQLdb. В котором, в свою очередь, создается обьект mysql от метода Connection. В модуле работа с базой данный осуществляется через курсоры, по этому создается курсор cursor и уже через него мы получаем доступ к базе. Создадим скрипт, который будет разбирать лог от запускаемого теста. После разбора лога, скрипт будет вносить изменения в таблицу sql. При чем не важно прошел тест или нет. Скрипт будет считывать название теста из командной строки, и запускать его. #!/usr/bin/python import os,sys, time, string , re import MySQLdb; def sqlinsert(time, testname, testnum, status, realise): query = 'INSERT INTO qastat(time, testname, testnum, status, realise) VALUES (\'' + time + '\', \'' \+ testname + '\', \'' + testnum + '\', \'' + status + '\', \'' + realise + '\')'; print query; mysql = MySQLdb.Connection('localhost', 'root', 'aa', 'rtest'); cursor = mysql.cursor(); cursor.execute(query); mysql.close(); return #set up realis realise = 0 test_pass = 0 test_fail = 0 #Название запускаемового теста будет передаваться из командной строки (f_in, f_out) = os.popen2(sys.argv[1:], 'b') a = f_out.readlines() f_in.close() f_out.close() #find test result for x in a: print x res = re.search("PASS: [A-Z]+-[0-9]* test passed",x) if(res): res = string.split(x, " ")[1] res = string.split(res,"-") testnum = res[-1] testname = str(res[0]) test_pass = 1 res = re.search("FAIL: [A-Z]+-[0-9]* test failed",x) if(res): res = string.split(x, " ")[1] res = string.split(res,"-") testnum = res[-1] testname = str(res[0]) test_fail = 1 print "testnum = %s" % testnum print "testname = %s" % testname result = "UNKNOWN" if (test_pass): result = "PASS" if (test_fail): result = "FAIL" print "result = %s" % (result) #get date #Логируем в базу дату в формате: год месяц день час минута секунда tm = time.localtime() date = "%.2d%.2d%.2d%.2d%.2d%.2d" % tm[:6] date = str(date) print "date = %s" % (date) print "realise = %s" % realise #insert values to sql database sqlinsert(date, testname, str(testnum), result, str(realise)) В результате выполнения мы видим, что тест прошел и его результат внесен в базу данных: debianm:/home/test/py# ./insert.py ./ltt-05.sh Simple test 1 2 3 PASS: LTT-05 test passed testnum = 05 testname = LTT result = PASS date = 20041207000048 realise = 0 INSERT INTO qastat(time, testname, testnum, status, realise) VALUES ('20041207000048', 'LTT', '05', 'PASS', '0') Теперь нужно считать данную информацию и попытаться сделать из нее выводы. В данном случае воспользуемся библиотекой matplotlib (matplotlib.sf.net) которая предоставляет хорошие возможности для отрисовки различных графиков и диаграмм. Для этого будем использовать следующую программу. #!/usr/bin/python import MySQLdb; import re from matplotlib.matlab import * # Функция sqlselect использует в качестве акгумента запрос к базе дынных query # и по умолчанию возвращает всю таблицу целиком. def sqlselect(query = 'SELECT * FROM qastat'): mysql = MySQLdb.Connection('localhost', 'root', 'aa', 'rtest') cursor = mysql.cursor() cursor.execute(query) result = cursor.fetchall() mysql.close() return result #Данная функция, используя matplotlib, рисует диаграмму относительно #переданных ей аргументов def drawchart(Values): ind = arange(len(Values)) width = 1 # the width of the bars p1 = bar( style='font-size: 9.0pt;'>ind , Values, width, color='y') ylabel('Values') title('Title') xticks( ind +width, ind ) savefig('barchart') show() return Допустим изначально значение таблицы sql равно: ('101101022004', 'LTT', '01', 'PASS', 1L) ('1111111111202', 'LTT', '02', 'PASS', 0L) ('1111111111202', 'LTT', '02', 'PASS', 0L) ('1111111111202', 'LTT', '02', 'PASS', 0L) ('1111111111202', 'LTT', '02', 'PASS', 0L) ('20041201004050', 'LTT', '06', 'FAIL', 0L) ('20041201004126', 'LTT', '06', 'FAIL', 0L) ('20041207000048', 'LTT', '05', 'PASS', 0L) ('20041207005605', 'LTT', '01', 'PASS', 0L) ('20041207005616', 'LTT', '02', 'PASS', 0L) ('20041207005622', 'LTT', '03', 'PASS', 0L) ('20041207005628', 'LTT', '04', 'PASS', 0L) ('20041207005639', 'LTT', '04', 'PASS', 0L) Тогда на bartchart.png (Рисунок 1) можно увидеть, что 2 тест является самым успешным (5 успешных запусков и ни одного не успешного.) Тест номер 6 является самым не успешным тестом (ни одного успешного теста и 2 теста провалено). #main # Данная функция просто подсчитывает число строк, возвращенное после запроса sqlselect() # Если входные параметры не заданы то будет возвращено общее число строк. С помощью данной # функции можно легко посчитать количество тестов заданных по определенному имени, дате или # номеру, лишь передав функции требуемый sql запрос. def countrow(): rownum = 0 for row in sqlselect(): rownum = rownum + 1 print "rownum = %d" % rownum return rownum #данная функция печатает всю таблицу def printall(): for row in sqlselect(): style='font-size: 9.0pt;'>print row return # Функция возвращает начальное и конечное время от соответствующего sql запроса. # Это может быть удобно например для узнавания начального и конечного времени тестирования # определенных тестов. def counttime(res): starttime = res[0][0] #print "starttime = %s" % starttime endtime = res[-1][0] return starttime,endtime print "Start time, End time = %s,%s" % counttime(sqlselect()) # Эта функция определяет количество пройденых и не пройденных тестов. # Рисует диаграму прохождения тестов в которой каждый пройденый тест # увеличивает вероятость а не пройденый уменьшает. В результате выполнения # будет графический файл в формате png. def getstatic(): res = sqlselect('SELECT testnum,status FROM qastat') rownum = 0 allpassed = 0 allfailed = 0 for row in res: rownum = rownum + 1 i = 0 val = range(rownum) for row in res: val[i] = 0 i = i + 1 i = 0 for row in res: res = re.search('PASS', row[1]) if (res): val[int(row[0])] = val[int(row[0])] + 1 allpassed = allpassed + 1 else: val[int(row[0])] = val[int(row[0])] - 1 allfailed = allfailed + 1 i = i - 1 print "Total test passed = %d" % allpassed print "Total test failed = %d" % allfailed return val Вызываем наши функции. countrow() ret = getstatic() drawchart(ret) Заключение. В данной статье я попытался создать систему для анализирования результатов тестирования, используя Python и Mysql. В результате появилась простая и прозрачная программа, которою можно легко изменить под свои конкретные нужды. Плюсами использования такого подхода к тестам является: простота использования, большая скорость работы, удобный вид полученной статистики, платформо-независимость тестирования, используется одна база данных. Минусами можно назвать: требование к наличию сконфигурированной базы Mysql, наличие установленного языка Python с модулями MySQLdb и matplotlib, применимость только к консольным тестам, с известным выводом. Необходимые для работы файлы: insert.py show_static.py |