Разделы портала

Онлайн-тренинги

.
Оценка и анализ рисков тестирования.
17.10.2008 15:21

Автор: Максим Уваров

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

Предполагается, что у вас есть набор тестов, которые представляют собой программу, выводящую слово 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