четверг, 31 января 2013 г.
среда, 30 января 2013 г.
About test plans
О тест планах.
Сам я совсем не сторонник больших тест планов,и увидев такую статью у Pradeep Soundararajan,не мог ей не поделиться.
- 98% тест планов никогда не обновляются и не поддерживаются в актуальном состоянии, короче говоря, о них просто забывают
- Первые 5 страниц тест плана содержат историю изменений, которая не интересует даже тех, чьи имена в ней перечислены
- Раздел описывающий цели тестирования является самой забавной частью этого документа. Порой, когда тестировщик сообщает о серьезной проблеме, кто-нибудь цитирует этот раздел, намекая на то, что находить проблемы такого рода не входило в его задачи и… Опа! Продукт и проблема живут дальше бок о бок
- Конечные пользователи могли бы экономить миллионы долларов, если бы компании, разрабатывающие ПО, перестали писать тест планы
- Не имеет значения насколько хорошо или плохо написан ваш тест план – тестировщики все равно будут писать тест кейсы так, как они считают нужным
- На проекте, который длится 4 года, никто и никогда не вспоминает о тест плане
- Тест план – отличный инструмент для аутсорсинговой компании, который позволяет потребовать денег у заказчика, не проводя при этом тестирование как таковое
- Каждый, кто принял участие в процессе написания тест плана, испытывает чувство глубочайшего удовлетворения в тот момент, когда этот документ завершен. При этом не важно, есть ли у них план тестирования или нет
- Стоимость рецензирования документа, который никто не собирается использовать, слишком высока
- Тот, кто считает, что он не готов к началу тестирования, поскольку у него нет тест плана, на самом деле не является тестировщиком
- Небольшой понятный тест план, который вы сможете поддерживать в актуальном состоянии, куда лучше, чем очень большой и подробный до которого никому нет дела
- Одна хорошая ассоциативная карта заменит вам тысячу прекрасных тест планов
- Тест план – это всегда документ, но далеко не всегда это план тестирования
- Заставляя тестировщика писать документ, который никто не будет читать, не надейтесь, что он вложит в его написание душу
- Некоторые тест планы устаревают еще до того момента, как в их черновике будет дописана последняя строчка
- Некоторые рецензенты добиваются того, чтобы тест план был идеален, при этом совсем не факт, что они знают хоть что-нибудь о разрабатываемом продукте
- Тот, кто знает, что такое альтернативная стоимость, скорее всего, напишет тест план куда лучше того, кто о ней не знает
Thusidides Selenium
Thusidides Selenium
Расскажу про небольшой Framework на Selenium.
Наверное все таки большинство из нас пользуется Selenium WD и знает что наверное самое слабое место у него-это отчеты(вообще говоря их вообще нет),для репортинга в основном используются или плагины или отчеты с Junit или TestNG(если это Java).
Так вот Thsidides призван внести в разработку тестов не только некоторые улучшения,но самое главное хорошие отчеты.
Немного истории!(надо ж как то разнообразить рассказ)
Thusidides переводится как Фукиди́д,был древнегреческим историком,который прославился именно качественными репортами,отсюда и название этого фреймворка.
История закончилась,перейдем к делу.
Тесты набрасывались для примера и чтобы просто показать принцип,естественно они не поддерживаемые и все такое.
Тесты набрасывались для примера и чтобы просто показать принцип,естественно они не поддерживаемые и все такое.
Для начала,чтобы подключить его к нашему проекту,добавим в Maven следующее:
4.0.0 webtests tests 1.0-SNAPSHOT jar wikipediawebtests http://maven.apache.org UTF-8 0.9.88 junit junit 4.8.2 org.hamcrest hamcrest-all 1.1 net.thucydides thucydides-junit ${thucydides.version} org.slf4j slf4j-simple 1.6.1 pom org.apache.maven.plugins maven-compiler-plugin 2.3.2 1.5 1.5 net.thucydides.maven.plugins maven-thucydides-plugin ${thucydides.version}
Все после добавления в проект,начнем разбираться как он нам предлагает структурировать наши тесты.
Вообще он больше рассчитан на приемочные тесты.Вы это сами заметите.
Создается общий класс для требований,фич и.т.д.
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(); } }
И привычная нам работа с WD с PageObject.
package Actions; public class BookPage extends PageObject { private ShareFunc func; @FindBy(linkText = "Все книги") WebElement allbooksButton; @FindBy(linkText = "Поиск") WebElement searchButton; @FindBy(name = "query") WebElement searchField; @FindBy(css = "button") WebElement searchBegin; @FindBy(linkText = "Большая книга кремлевских тайн.") WebElement searchBook; @FindBy(linkText = "Каталог") WebElement catalog; @FindBy(linkText = "Малая форма") WebElement littleForm; @FindBy(linkText = "Повести") WebElement story; @FindBy(linkText = "Всего лишь капелька яда") WebElement oneOfStories; public BookPage(WebDriver driver) { super(driver); func=new ShareFunc(driver); } public void getMainPage(String url) { getDriver().get(url); } public void allBooks() { func.safeClick(allbooksButton); } public void search(String searchWord) { func.safeClick(searchButton); func.sendKeys(searchField, searchWord); func.safeClick(searchBegin); func.safeClick(searchBook); } public void catalog() { func.safeClick(catalog,getDriver()); assertEquals("КАТАЛОГ",findElement(By.cssSelector("div.title > strong")).getText()); func.safeClick(littleForm); assertEquals("МАЛАЯ ФОРМА",findElement(By.cssSelector("div.title > strong")).getText()); func.safeClick(story, getDriver()); assertEquals("МАЛАЯ ФОРМА - ПОВЕСТИ",findElement(By.cssSelector("div.title > strong")).getText()); func.safeClick(oneOfStories); assertTrue(getPageSource().contains("Марина Серова")); } }Иерархия тестов примерно такая,сначала мне не понравилась,но если с ней поработать,то весьма удобно.
Далее в запускаем через maven
mvn test thucydides:aggregate
И он нам забацает вот такой отчет.
Немного о более тонкой настройке:
Для выбора запуска браузера запускаем Maven с опцией:
-Dwebdriver.driver=chrome
А для параллельного запсука тестов например используем сигнатуры перед классом с тестами
@RunWith(ThucydidesParameterizedRunner.class)
@Concurrent(threads="3")
понедельник, 21 января 2013 г.
Testing SMS and USSD with AT commands
Тестирование через SMS и USSD(с использованием AT комманд)
Бывают такие задачи,когда использование только программной части реализации нам недостаточно,скажем когда при регистрации код подтверждения отправляется нам в смс,автоматизировать такой сценарий-сложно,но можно)
Собственно для автоматизации таких сценариев с использованием мобильных устройств нам понадобится 3g модем,а все остальное я расскажу здесь.
Возможно получится большая статья,но это для того чтобы команда "Р" все сделала правильно!)
Начнем.
Для начала,нам потребуется Java библиотека отсюда-http://smslib.org/
Она будет осуществлять основное взаимодействие между мобильным устройством и нашим тестом.
Поподробнее об установке:
Для корректной работы,этой библиотеки нам понадобятся следующие зависимости:
1.RxTx который можно взять здесь.
После этого
- Файл
RXTXcomm.jar
нужно положитьJDKDIR/jre/lib/ext/
- А нужную библиотеку (для Linux 32bit librxtxSerial.so) в
JDKDIR/jre/bin/
2.Smslib использует log4j в качестве логгера,ставим его.
Скачиваем log4j отсюда. А файл
log4j-1.2.15.jar
помещаем в ваш classpath или в Java's lib/ext.
3.Apache Jakarta Commons - NET
Скачиваем Apache Jakarta Commons/NET отсюда. А файл commons-net-2.0.jar помещаем в ваш classpath или в Java's lib/ext.
4.JSMPP Library
То же самое с JSMPP ,скачиваем отсюда и jsmpp-2.1.0.jar помещаем в ваш classpath или в Java's lib/ext.
Скачиваем Apache Jakarta Commons/NET отсюда. А файл commons-net-2.0.jar помещаем в ваш classpath или в Java's lib/ext.
4.JSMPP Library
То же самое с JSMPP ,скачиваем отсюда и jsmpp-2.1.0.jar помещаем в ваш classpath или в Java's lib/ext.
Тем,кто больше любит Maven-вот пожалуйста
org.rxtx rxtx 2.1.7 org.smslib smslib 3.5.2 log4j log4j 1.2.17 nightlabs nightlabs repo http://smslib.googlecode.com/svn/maven2/
Подключаем модем в 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(); } } }
четверг, 17 января 2013 г.
Подсчет необходимого количества виртуальных пользователей.
Подсчет необходимого количества виртуальных пользователей.
Начнем с примера:
На сайте 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 минут.
С таким раскладом наш тест,даст нам не адекватные данные.
Поэтому для надежности,обязательно смотрим логи сервера,, чтобы получить определение пика пользователей в промежуток времени.
Не забываем про:http://adeptqa.blogspot.ru/2012/11/blogeratorru.html
А кому лень считать самому: используем-http://www.webperformance.com/library/tutorials/CalculateNumberOfLoadtestUsers/
четверг, 10 января 2013 г.
Кодировка ответа Sampler в Jmeter.
Кодировка ответа Sampler в Jmeter.
Иногда при работе с 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) доступ к текущему событию.
с кодом
prev.setDataEncoding("UTF-8");
И все.Был рад помочь.
Подписаться на:
Сообщения (Atom)