Сам я совсем не сторонник больших тест планов,и увидев такую статью у Pradeep Soundararajan,не мог ей не поделиться.
98% тест планов никогда не обновляются и не поддерживаются в актуальном состоянии, короче говоря, о них просто забывают
Первые 5 страниц тест плана содержат историю изменений, которая не интересует даже тех, чьи имена в ней перечислены
Раздел описывающий цели тестирования является самой забавной частью
этого документа. Порой, когда тестировщик сообщает о серьезной
проблеме, кто-нибудь цитирует этот раздел, намекая на то, что находить
проблемы такого рода не входило в его задачи и… Опа! Продукт и проблема
живут дальше бок о бок
Конечные пользователи могли бы экономить миллионы долларов, если бы компании, разрабатывающие ПО, перестали писать тест планы
Не имеет значения насколько хорошо или плохо написан ваш тест план –
тестировщики все равно будут писать тест кейсы так, как они считают
нужным
На проекте, который длится 4 года, никто и никогда не вспоминает о тест плане
Тест план – отличный инструмент для аутсорсинговой компании, который
позволяет потребовать денег у заказчика, не проводя при этом
тестирование как таковое
Каждый, кто принял участие в процессе написания тест плана,
испытывает чувство глубочайшего удовлетворения в тот момент, когда этот
документ завершен. При этом не важно, есть ли у них план тестирования
или нет
Стоимость рецензирования документа, который никто не собирается использовать, слишком высока
Тот, кто считает, что он не готов к началу тестирования, поскольку у
него нет тест плана, на самом деле не является тестировщиком
Небольшой понятный тест план, который вы сможете поддерживать в
актуальном состоянии, куда лучше, чем очень большой и подробный до
которого никому нет дела
Одна хорошая ассоциативная карта заменит вам тысячу прекрасных тест планов
Тест план – это всегда документ, но далеко не всегда это план тестирования
Заставляя тестировщика писать документ, который никто не будет читать, не надейтесь, что он вложит в его написание душу
Некоторые тест планы устаревают еще до того момента, как в их черновике будет дописана последняя строчка
Некоторые рецензенты добиваются того, чтобы тест план был идеален,
при этом совсем не факт, что они знают хоть что-нибудь о разрабатываемом
продукте
Тот, кто знает, что такое альтернативная стоимость, скорее всего, напишет тест план куда лучше того, кто о ней не знает
Наверное все таки большинство из нас пользуется Selenium WD и знает что наверное самое слабое место у него-это отчеты(вообще говоря их вообще нет),для репортинга в основном используются или плагины или отчеты с Junit или TestNG(если это Java).
Так вот Thsidides призван внести в разработку тестов не только некоторые улучшения,но самое главное хорошие отчеты.
Немного истории!(надо ж как то разнообразить рассказ)
Thusidides переводится как Фукиди́д,был древнегреческим историком,который прославился именно качественными репортами,отсюда и название этого фреймворка.
История закончилась,перейдем к делу.
Тесты набрасывались для примера и чтобы просто показать принцип,естественно они не поддерживаемые и все такое.
Для начала,чтобы подключить его к нашему проекту,добавим в Maven следующее:
Все после добавления в проект,начнем разбираться как он нам предлагает структурировать наши тесты.
Вообще он больше рассчитан на приемочные тесты.Вы это сами заметите.
Создается общий класс для требований,фич и.т.д.
public class Application {
@Feature
public class TestPageBook {}
public class TestAuth{}
public class TestSearch{}
}
Как мы видим,в одной фиче может быть несколько классов и конечно может быть несколько фич.
После этого создается класс с тестом
@RunWith(ThucydidesRunner.class)
@Story(Application.TestPageBook.class)
public class TestPageBook {
@Managed
public WebDriver driver;
@ManagedPages(defaultUrl = "http://m.megafonpro.ru/")
public Pages pages;
@Steps
public StepsinBook book;
@Test
public void testBook() throws Exception {
book.getMain("http://m.megafonpro.ru/"); //Вход на главную страницу тестируемого ресурса
book.AllBooks(); //Переход во вкладку Все книги
book.search(); //Поиск книги с поисковым выражением "Книга
book.catalog(); //Проверка каталога книг по жанрам
}
@Pending @Test //данная аннотация значит,что тест еще не имплементирован
public void testBuyBook(){}
}
Далее описываются шаги самого теста,Steps как они называются здесь.
public class StepsinBook extends ScenarioSteps {
public StepsinBook(Pages pages) {
super(pages);
}
public BookPage getPageBook()
{
return getPages().currentPageAt(BookPage.class);
}
@Step
public void getMain(String url)
{
getPageBook().getMainPage(url);
}
@Step
public void AllBooks()
{
getPageBook().allBooks();
}
@Step
public void search(){
getPageBook().search("Книга");
}
@Step
public void catalog(){
getPageBook().catalog();
}
}
Тестирование через SMS и USSD(с использованием AT комманд)
Бывают такие задачи,когда использование только программной части реализации нам недостаточно,скажем когда при регистрации код подтверждения отправляется нам в смс,автоматизировать такой сценарий-сложно,но можно)
Собственно для автоматизации таких сценариев с использованием мобильных устройств нам понадобится 3g модем,а все остальное я расскажу здесь.
Возможно получится большая статья,но это для того чтобы команда "Р" все сделала правильно!)
Подключаем модем в USB порт.
Вводим lsub
для вывода подключенных устройств,ищем там свой модем.
Теперь даем права на чтение и запись с портов:
sudo chmod o+rw /dev/ttyU*
В Java коде пробуем исполнить следующий код(Обращаю внимание,что код не мой,ибо он дико вырвиглазный,а код из доков самой библиотеки):
import org.smslib.helper.CommPortIdentifier;
import org.smslib.helper.SerialPort;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Formatter;
public class CommTest
{
private static final String _NO_DEVICE_FOUND = " no device found";
private final static Formatter _formatter = new Formatter(System.out);
static CommPortIdentifier portId;
static Enumeration portList;
static int bauds[] = { 9600, 14400, 19200, 28800, 33600, 38400, 56000, 57600, 115200 };
private static Enumeration getCleanPortIdentifiers()
{
return CommPortIdentifier.getPortIdentifiers();
}
public static void main(String[] args)
{
System.out.println("\nSearching for devices...");
portList = getCleanPortIdentifiers();
while (portList.hasMoreElements())
{
portId = portList.nextElement();
if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL)
{
_formatter.format("%nFound port: %-5s%n", portId.getName());
for (int i = 0; i < bauds.length; i++)
{
SerialPort serialPort = null;
_formatter.format(" Trying at %6d...", bauds[i]);
try
{
InputStream inStream;
OutputStream outStream;
int c;
String response;
serialPort = portId.open("SMSLibCommTester", 1971);
serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_IN);
serialPort.setSerialPortParams(bauds[i], SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
inStream = serialPort.getInputStream();
outStream = serialPort.getOutputStream();
serialPort.enableReceiveTimeout(1000);
c = inStream.read();
while (c != -1)
c = inStream.read();
outStream.write('A');
outStream.write('T');
outStream.write('\r');
Thread.sleep(1000);
response = "";
StringBuilder sb = new StringBuilder();
c = inStream.read();
while (c != -1)
{
sb.append((char) c);
c = inStream.read();
}
response = sb.toString();
if (response.indexOf("OK") >= 0)
{
try
{
System.out.print(" Getting Info...");
outStream.write('A');
outStream.write('T');
outStream.write('+');
outStream.write('C');
outStream.write('G');
outStream.write('M');
outStream.write('M');
outStream.write('\r');
response = "";
c = inStream.read();
while (c != -1)
{
response += (char) c;
c = inStream.read();
}
System.out.println(" Found: " + response.replaceAll("\\s+OK\\s+", "").replaceAll("\n", "").replaceAll("\r", ""));
}
catch (Exception e)
{
System.out.println(_NO_DEVICE_FOUND);
}
}
else
{
System.out.println(_NO_DEVICE_FOUND);
}
}
catch (Exception e)
{
System.out.print(_NO_DEVICE_FOUND);
Throwable cause = e;
while (cause.getCause() != null)
{
cause = cause.getCause();
}
System.out.println(" (" + cause.getMessage() + ")");
}
finally
{
if (serialPort != null)
{
serialPort.close();
}
}
}
}
}
System.out.println("\nTest complete.");
}
}
Должны в выводе получить,что то вроде:
Trying at 9600... Getting Info... Found:
Trying at 14400... no device found (
Trying at 19200... Getting Info... Found: E352
Trying at 28800... no device found
Trying at 33600... no device found
Так,если что то нашли,мы на пути к победе.
Дальше все проще будет.
Расскажу на примере получения входящих смс с Java кода.
import org.smslib.AGateway;
import org.smslib.AGateway.Protocols;
import org.smslib.IInboundMessageNotification;
import org.smslib.InboundMessage;
import org.smslib.InboundMessage.MessageClasses;
import org.smslib.Message.MessageTypes;
import org.smslib.Service;
import org.smslib.modem.SerialModemGateway;
import java.util.ArrayList;
import java.util.List;
public class ReadMessages
{
public void doIt() throws Exception
{
List msgList; //Объявление списка входящих смс
InboundNotification inboundNotification = new InboundNotification(); //создание метода для оповещений и статуса входящих смс
try
{
// Создаем объект нашего модема,по полученным выше данным
SerialModemGateway gateway = new SerialModemGateway("modem", "/dev/ttyUSB2", 9600, "Huawei", "E352");
//Задаем протокол модема,по дефолту PDU,но можно указать и TEXT
gateway.setProtocol(Protocols.PDU);
// Мы хотим чтобы модем работал со входящими?
gateway.setInbound(true);
//Мы хотим чтобы модем работал с исходящими?
gateway.setOutbound(true);
// Указываем SIM PIN.
gateway.setSimPin("0000");
// Сеттим наши оповещения
Service.getInstance().setInboundMessageNotification(inboundNotification);
// Добавляем сервису наш объект модема
Service.getInstance().addGateway(gateway);
// Стартуем сервис
Service.getInstance().startService();
// Можно вывести основную инфу(если нужно)
System.out.println();
System.out.println("Modem Information:");
System.out.println(" Manufacturer: " + gateway.getManufacturer());
System.out.println(" Model: " + gateway.getModel());
System.out.println(" Serial No: " + gateway.getSerialNo());
System.out.println(" SIM IMSI: " + gateway.getImsi());
System.out.println(" Signal Level: " + gateway.getSignalLevel() + " dBm");
System.out.println(" Battery Level: " + gateway.getBatteryLevel() + "%");
System.out.println();
msgList = new ArrayList();
//Читаем смс
Service.getInstance().readMessages(msgList, MessageClasses.ALL);
for (InboundMessage msg : msgList)
System.out.println(msg);
System.out.println("Now Sleeping - Hit to stop service.");
System.in.read();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
Service.getInstance().stopService();
}
}
public class InboundNotification implements IInboundMessageNotification
{
public void process(AGateway gateway, MessageTypes msgType, InboundMessage msg)
{
if (msgType == MessageTypes.INBOUND) System.out.println(">>> New Inbound message detected from Gateway: " + gateway.getGatewayId());
else if (msgType == MessageTypes.STATUSREPORT) System.out.println(">>> New Inbound Status Report message detected from Gateway: " + gateway.getGatewayId());
System.out.println(msg);
}
}
public static void main(String args[])
{
ReadMessages app = new ReadMessages();
try
{
app.doIt();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
Вроде в комментариях все понятно,но если что спрашиваем в комментах.
И пример с USSD(как же без него)
import org.ajwcc.pduUtils.gsm3040.PduUtils;
import org.smslib.*;
import org.smslib.AGateway.Protocols;
import org.smslib.modem.SerialModemGateway;
public class SendUSSD
{
public void doIt() throws Exception
{
USSDNotification ussdNotification = new USSDNotification();
System.out.println("Example: Send USSD Command from a serial gsm modem.");
System.out.println(Library.getLibraryDescription());
System.out.println("Version: " + Library.getLibraryVersion());
SerialModemGateway gateway = new SerialModemGateway("modem.com35", "/dev/ttyUSB2", 9600, "Huawei", "E352");
gateway.setProtocol(Protocols.PDU);
gateway.setInbound(true);
gateway.setOutbound(true);
gateway.setSimPin("0000");
Service.getInstance().addGateway(gateway);
Service.getInstance().setUSSDNotification(ussdNotification);
Service.getInstance().startService();
byte[] dataToSend = null;
byte[] biteToSend = null;
String stringtoSend = "*100#";
dataToSend = PduUtils.stringToUnencodedSeptets(stringtoSend);
biteToSend = PduUtils.encode7bitUserData(null, dataToSend);
stringtoSend = PduUtils.bytesToPdu(biteToSend);
System.out.println(stringtoSend);
// String resp = gateway.sendUSSDCommand(stringtoSend); //вообще можно отправлять так,но у меня не заработало,поэтому ниже работает)
String resp = gateway.sendCustomATCommand("AT+CUSD=1,\""+stringtoSend+"\",0\r");
System.out.println(resp);
System.out.println("Now Sleeping - Hit to terminate.");
System.in.read();
Service.getInstance().stopService();
}
public class USSDNotification implements IUSSDNotification
{
public void process(AGateway gateway, USSDResponse response) {
System.out.println("USSD handler called from Gateway: " + gateway.getGatewayId());
System.out.println(response);
}
}
public static void main(String args[])
{
SendUSSD app = new SendUSSD();
try
{
app.doIt();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
Вот и все,не боимся автоматизировать,все что можно.
Подсчет необходимого количества виртуальных пользователей.
Начнем с примера:
На сайте Google Analytics в средний загруженности день имеем:
2000 посетителей в 60 минут
10,000 просмотров страниц
среднее время нахождение на сайте 7 минут
Так,я хочу вывести,сколько виртуальных пользователей мне понадобится для адекватной нагрузки?
Начнем:
2000 пользователей в 1 час (60 минут), 7 минут на сайте.
60 минут / 7минут = 8.5 (пользователей в одном потоке)
2000 / 8.5 = 235 пользователей нужно всего сэмулировать.
На первый взгляд такой расчет кажется весьма логичным и применимым к нашей задаче.Каждый тестовый сценарий должен иметь среднюю продолжительность в 7 минут,чтобы отразить среднее время пребывания на сайте.То есть около 9 пользователей полностью пройдут сценарий в течении часа,т.к. наши пользователи находятся на сайте всего по 7 минут,нам не нужно проверять все 2000 пользователей одновременно.Они долго не задерживаются, поэтому, разделив 2000 пользователей по 8,5 (продолжительность визитов) получим, что около 235 пользователей.
Если представить это в виде формулы:
U = V / (60 / D)
Где:
U-это количество виртуальных пользователей (это то, что мы пытаемся выяснить)
V- это среднее количество посетителей в час
D-является средней продолжительности посетителя
60- это количество минут в часе))
Основной недостаток этого подхода,это то что мы ожидаем равномерного прибытия пользователей,что никак не гарантированно)
Вполне возможно, что из 2000 посетителей в час, 900 из них придут на сайт в первые 10 минут, затем 400 пользователей в 40 минут, а затем 700 пользователей за последние 10 минут.
С таким раскладом наш тест,даст нам не адекватные данные.
Поэтому для надежности,обязательно смотрим логи сервера,, чтобы получить определение пика пользователей в промежуток времени.
Иногда при работе с Jmeter,в ответах на ваши запросы он использует неправильную кодировку.
Дело в том,что можно поставить кодировку контента(то бишь отправляемых значений),а получаемых нет.
Это можно исправить(правда способ костыльный,хотя рабочий)-используем BeanShell Jmeter.
Небольшая справка о переменных Jmeter в BeanShell для тех кто не в курсе:
log - (Logger) - Можно использовать для записи лога в файл.
ctx - (JMeterContext) - Доступ к контексту.
vars - (
JMeterVariables
) - Позволяет читать/записывать значения в переменные Jmeter: vars.get(key); vars.put(key,val); vars.putObject("OBJ1",new Object());
props - (JMeterProperties - class java.util.Properties) -собственно класс свойств из Java props.get("START.HMS"); props.put("PROP1","1234");
prev - (SampleResult) - Доступ к предыдущему ответу.
sampleEvent (SampleEvent) доступ к текущему событию.
Собственно,если мы хотим поменять кодировку ответа на определенный запрос,то к запросу добавляем BS PostProcessor
с кодом