29 Июнь 2009 г.

Константы в JAVA

В который раз уже задался странным вопросом: "Как лучше хранить и передавать значение констант в JAVA?"

В итоге покопавшись в интернете нашел 3 способа:

1. Передача через статические переменные в классе:

public class ConstantClass {
public static final String CONSTANT = "TEST";
...
}

2. Передача через интерфейсы:
public interface ConstantImpl {
public static String CONSTANT = "TEST";
...
}

3. Передача через Enums:
public enum ConstantEnum {
CONSTANT ("TEST");
private final String constant;

ConstantEnum(String constant) {
this.constant = constant;
}

public String constant() {
return constant;
}
}

4. Специфицирование объекта как "const". Может быть использовано только по отношению к объектам, применение его к примитивам (int, byte, long и т.д.) приведет к ошибке компиляции. Но это лишь миф!!! До сих пор данная фича не используется и существует только лишь в теории: A Comprehensive Theory of Adding 'const' to Java

Давайте рассмотрим, как будет выглядеть получение значения константы для каждого из перечисленных методов:
1. String constantClass = ConstantClass.CONSTANT;

2. Сперва надо имплементировать интерфейс:
class ConstantTest implements ConstantImpl, и тогда можно будет явно вызвать константу следующим образом:
String constantInterface = CONSTANT;

3. String constantEnum = ConstantEnum.CONSTANT.constant();

А теперь давайте разберемся чем же хороши или плохи данные реализации передачи констант:
1. Передача через статические переменные в классе
Никаких серьезных минусов мной не обнаружено... ищу...

2. Передача через интерфейсы
Основной минус, обнаруженный на страницах интернета - данная реализация это АНТИПАТТЕРН. Но чем он плох? Об этом никто не высказался. Из своих наблюдений могу сказать лишь одно неудобство: если вы используете несколько интерфейсов с константами, имеющими одинаковые имена, и потом имплементируете их в один класс, то Вам придется в любом случае конкретизировать какую из них вы желаете использовать. Лично мне это показалось неудобным, а кому-то это будет нормально. Каждому своё!

3. Передача через Enums
На сколько я понял именно этот механизм был разработан специально для определения и передачи констант. Т.е. подобная реализация сделана по науке.

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

Спасибо за внимание.



Читать далее...

22 Июнь 2009 г.

WATIJ закройся... Трах-тибидох-тибидох

И снова, здравствуйте!

Начнем же день, с еще одного бага в очередном инструменте тестирования, который Ваш покорный слуга использует в своих исследованиях - это WATIJ.

Итак, бага...
Короткое описание
WATIJ зависает при попытке закрыть браузер

Платформа
WinXP SP3, java 1.6.0_10, IE6,
Watij Release 3.2.1

Шаги для воспроизведения
1. Выполняем следующий код

IE ie = new IE();
ie.start();
ie.goTo("http://www.protesting.ru/contact/contact.html");
ie.selectList(FinderFactory.name("type")).focus();
ie.close();
System.out.println("End");

Результат
Окно браузера закрылось, но java процесс висит.

Ожидаемый результат
Окно закрылось, java процесс завершился

Постскриптум
Как ни странно, проблема воспроизводиться не на всех компах :( Просил воспроизвести товарища, у него все ГУД...

Может кто-нить встречался с подобным поведением???

PS на всякий случай накатал багу в саппорт WATIJа: https://sourceforge.net/tracker/?func=detail&aid=2809097&group_id=165206&atid=834713

Спасибо,
Всегда Ваш - Я
Да хранит Вас WWW

Читать далее...

11 Июнь 2009 г.

htmlunit WebRequestSettings - встали и вышли...

Добрый день, дорогие блогочитатели.
Мы снова с Вами. Борьба с некачественным софтом продолжается. Предлагаю сегодня рассмотреть интересную багу, найденную Вашим покорным слугой, в инструменте для автоматизированного тестирования. На этот раз это - HtmlUnit.
Итак, багу в студию (под звук фанфар в зал вносят тело клиента)...

htmlunit-2.5

Короткое описание
При клике на линк и открытии страницы теряются установленные первоначально параметры запроса (WebRequestSettings)


Шаги для воспроизведения
1. Пишем следующий код
WebClient client = new WebClient();
client.setJavaScriptEnabled(false);
WebRequestSettings settings = new WebRequestSettings(new URL("http://www.protesting.ru"));
settings.setCharset("utf-8");
client.getPage(settings);

HtmlPage page = (HtmlPage) client.getCurrentWindow().getEnclosedPage();
List list = page.getAnchors();
for (int i = 0; i < list.size(); i++) {
HtmlAnchor htmlAnchor = (HtmlAnchor) list.get(i);
if (htmlAnchor.getHrefAttribute().equals("./automation/")) {
try {
page = htmlAnchor.click();
break;
} catch (IOException e) {
e.printStackTrace();
}
}
}
String title = page.getTitleText();
System.out.println(title);

2. Выполняем его

Результат
Про Те�тинг - �втоматиЕированное те�тирование программного обе�печени� (Software Test Automation)

Ожидаемый результат
Про Тестинг - Автоматизированное тестирование программного обеспечения (Software Test Automation)

Примечание
Копнул немного глубже, и могу поделиться тем, что я увидел внутри.
В нашей имплементации, мы создаем WebRequestSettings setting и в нем явно указываем кодировку settings.setCharset("utf-8"). WebClient открывает страницу и уже знает, что она будет идти в кодировке utf-8.
Если мы не будем создавать свои WebRequestSettings, то htmlunit выставит charset по умолчанию (charset_ = TextUtil.DEFAULT_CHARSET = "ISO-8859-1"), и тогда уже начиная с первой страницы будут грабли.

Дальше интереснее.
Мы получили страницу с явно указаной кодировкой UTF-8 и делаем клик по ссылке, что явно должно подразумевать, что параметры запроса не изменились, делаем клик и получаем страницу с крокозябрами вместо русского текста.
Идем в класс HtmlAnchor и видим там следующий код:
protected Page doClickAction(final Page defaultPage, final String hrefSuffix) throws IOException {
final String href = getHrefAttribute() + hrefSuffix;

if (mainLog_.isDebugEnabled()) {
mainLog_.debug(
"do click action in window '"
+ defaultPage.getEnclosingWindow().getName()
+ "', using href '" + href + "'");
}

if (href.length() > 0 && !href.startsWith("#")) {
final HtmlPage page = (HtmlPage) getPage();
if (TextUtil.startsWithIgnoreCase(href, JAVASCRIPT_PREFIX)) {
return page.executeJavaScriptIfPossible(
href, "javascript url", getStartLineNumber()).getNewPage();
}
final URL url = page.getFullyQualifiedUrl(href);
final WebRequestSettings settings = new WebRequestSettings(url);
settings.addAdditionalHeader("Referer", page.getWebResponse().getRequestUrl().toExternalForm());
if (mainLog_.isDebugEnabled()) {
mainLog_.debug(
"Getting page for " + url.toExternalForm()
+ ", derived from href '" + href
+ "', using the originating URL "
+ page.getWebResponse().getRequestUrl());
}
return page.getWebClient().getPage(
page.getEnclosingWindow(),
page.getResolvedTarget(getTargetAttribute()),
settings);
}
return defaultPage;
}

О ужас:
final WebRequestSettings settings = new WebRequestSettings(url);

WebRequestSettings пересоздаются при клике на ссылке и все наши настройки теряются.

Немного поиграв, я заменил эту строку на следующие:
final WebRequestSettings settings = page.getWebResponse().getRequestSettings();
settings.setUrl(url);

И перезапустил код из примера.

Результат:
Про Тестинг - Автоматизированное тестирование программного обеспечения (Software Test Automation)


Что и требовалось ожидать.

Бага в саппорт htmlunit написана, ждем ответа!
https://sourceforge.net/tracker/?func=detail&aid=2804850&group_id=47038&atid=448266

PS Если кто знает, как в htmlunit можно глобально установить параметры кодировки запроса и ответа от сервера, буду премного благодарен.

Оставайтесь с нами,
Всегда Ваш - Я

Да хранит Вас WWW.

Читать далее...

2 Июнь 2009 г.

Выбор элемента из списка - просто, но не для Watij

"Выбор элемента из списка" - Казалось бы простая задача для любого инструмента, но и тут наш друг опенсорс Watij проявил свою несостоятельность.

Приведем пример:

* HTML код страницы

<select name="criteria" onchange="change(this.value)">
<option value="-2">Id</option>
<option value="-1">Application Number</option>
<option value="0" selected="selected">other</option>
</select>


* java код с выбором элемента (допускаем, что элемент есть всегда, по этой причине не проверяем ничего на наличие, а сразу выбираем элемент)

SelectList combobox = selectList(FinderFactory.name("criteria"));
combobox.option(FinderFactory.text("Id")).select();


* ошибка в результате выполнения

ERROR: 02/06/2009 14:30:10 [com.jniwrapper.util.c] - DomFactory.Proxy.invoke: target = Element <SELECT> id: >ms__id1, method = fireEvent, error = null
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.jniwrapper.win32.ie.dom.j.run(SourceFile:592)
at com.jniwrapper.win32.MessageLoopThread$ThreadSynchronizedAction.run(MessageLoopThread.java:566)
at com.jniwrapper.win32.MessageLoopThread$LoopThread.run(MessageLoopThread.java:508)
Caused by: com.jniwrapper.win32.com.ComException: COM object method returns error code: 0x80070057; E_INVALIDARG (The parameter is incorrect.)
at com.jniwrapper.win32.com.impl.IUnknownImpl.invokeStandardVirtualMethod(SourceFile:725)
at com.jniwrapper.win32.mshtml.impl.IHTMLElement3Impl.fireEvent(IHTMLElement3Impl.java:376)
at com.jniwrapper.win32.ie.dom.c.fireEvent(SourceFile:322)
at com.jniwrapper.win32.ie.dom.c.fireEvent(SourceFile:558)
... 7 more


При этом элемент выбирается, но событие onchange не отрабатывает.
Попробовал убрать onchange из описания SELECT, запустил тотже кусок java кода... В результате - та же самая ошибка.

Начал копаться в интернете, нашел пару братьев по несчастью, с той же проблемой. Думаю: "Что делать? Может обновить библиотеку jniwrapper?". В моей версии Watij она jniwrap-3.6.1.jar, в интернете нашел jniwrap-3.7.3.jar.
Скачал.
Запускаю - даже браузер не стартанул... Начал смотреть где беда, нашел быстро:
оказывается библиотека jniwrap платная... и при запуске проверяет лицензионный ключик :) (Интересная получается штука, бесплатный инструмент использует платную библиотеку) На сколько это правильно не знаю, но звучит очень не логично...

А теперь ВНИМАНИЕ вопрос:
Кто знает как победить проблему выбора элемента из списка?

Всем ответившим, заранее спасибо.

P.S. Но если вопрос окажется не решаемым, то можно ставить большой и жирный крест на таком "замечательном" инструменте, как Watij...

Читать далее...

27 Май 2009 г.

Поиск проверять-то надо...

Добрый день, дамы и господа. Мы рады приветствовать вас при очередном описании, очередного бага, найденного на просторах всемирной паутины...
Итак, начнем:

САЙТ: http://www.ameno.ru/
КОРОТКОЕ ОПИСАНИЕ: Поиск с главной страницы в соннике, с использованием русских слов, работает не правильно.

ШАГИ ДЛЯ ВОСПРОИЗВЕДЕНИЯ:
1. Открываем главную страницу сайта
--> Внизу страницы находим раздел: СОННИК (см копию экрана - выделено желтой рамкой)3. Введите поисковое слово, например "ночь"
4. Нажмите кнопку "Найти"

РЕЗУЛЬТАТ:
Запрос не прошел валидацию. (смотри копию экрана)

ОЖИДАЕМЫЙ РЕЗУЛЬТАТ:
Поиск прошел удачно, описание требуемого сна показано верно.

ПРИМЕЧАНИЕ 1: Дальнейшее использование поиска, со страницы результатов поиска, происходит правильно.
ПРИМЕЧАНИЕ 2:Аналогично поиску в соннике, неправильно работает и поиск в разделе ТАЙНА ИМЕНИ

И теперь немного отсебятины.
Формируется запрос:
http://www.ameno.ru/private_life/sonnik/search/?Q=%ED%EE%F7%FC
которые влечет за собой редирект:
Redirect to: http://www.ameno.ru/magic/sonnik/search/?Q=%25ED%25EE%25F7%25FC
(обратите внимание, что РЕДИРЕКТ ЕЩЕ РАЗ ЗАКОДИРОВАЛСЯ)
В результате из-за этой двойной кодировки и получается найденная бага... Если открыть URL редиректа, без второй кодировки:
http://www.ameno.ru/magic/sonnik/search/?Q=%ED%EE%F7%FC
то все будет работать правильно:



P.S. Багу зарегистрировал на сайте, ждемс фикса.


Спасибо за внимание,
До следующих встреч.

Да хранит Вас всемогущий WWW

Читать далее...

7 Май 2009 г.

Борьба со спам блогами

Получил сегодня на ящик сообщение подобного плана:


Хочется узнать у Вас, мои уважаемые читатели, считаете ли вы, что я тут слишком много спамлю? :)

Заранее спасибо за Ваши теплые отзывы и комментарии.

Всегда Ваш,
Алексей Булат


Читать далее...

6 Май 2009 г.

Фильтровать не перефильтровать или как тестировать поиск

Наверное, нет сейчас человека, который бы не ругался на разработчиков софта из-за ошибок при поиске или фильтрации данных. Вам надо что-то найти, а приложение либо показывает не то что надо, либо выдает какие-то ошибки. В этом случае вы говорите: "Программисты опять накосячили." Хочу перефразировать: "Тестеры не до тестировали", т.к. программист имеет право на ошибку, а вот тестер уже нет. Он - последний оплот качества :) Ладно, перейдем от слов к делу.

Для начала рассмотрим багу из жизни. Решил я поискать себе квартирку, пошел на сайт, задал значения фильтра и получил не совсем то, что ожидал.



Сайт realt.by
Пример неправильной работе фильтра на странице "Квартиры в новостройках, долевое строительство квартир":

Краткое Описание:
Неправильно реализован поиск по условию "Срок сдачи: до"

Пример:
1. выбираем Срок сдачи: до 3 квартал 2009
2. Нажимаем "Начать Поиск"

Результат:
Список объектов со сроком сдачи 3 квартал 2009

Ожидаемый Результат:
Список объектов со сроком сдачи, начиная с сегодняшнего дня до 3 квартала 2009


Теперь приведем некоторые рекомендации по тестированию фильтров и поиска, используя элементарные примеры.

Пример 1. Поиск пользователя


Имеющийся фильтр: Имя, Фамилия, Возраст, Место работы.
Тестовый пользователь: Иван, Иванов, 25, Смольный.

Случаи тестирования
1. Заполните в фильтре все поля, соответственно. В результате вы должны получить только одну запись, а именно тестового пользователя: Иванова Ивана. Если вы получили ни одного или несколько пользователей, и кто-то из них не Иванов Иван 25ти лет, работающий в Смольном, значит это бага.
2. Поиск по отдельным полям. Введите в фильтре только имя "Иван". В результате вы получаете список пользователей с именем "Иван", причем это должны быть все пользователи с этим именем, а не только некоторые из них. Повторите тоже самое с остальными полями фильтра.
3. Поиск используя комбинации полей. Введите в фильтре имя "Иван" и фамилию "Иванов". В результате вы получаете список всех пользователей с именем "Иван" и фамилией "Иванов". Повторите тоже самое варьируя комбинациями полей фильтра, например: (имя, возраст), (фамилия, место работы), (фамилия, возраст, место работы) и т.д.

Примечание: Если какое-либо поле содержит специальные (ASCII) символы, то это не должно искажать результаты поиска, как в функциональном плане, так и при выводе их на экран (XSS и SQL Injections).

Пример 2. Поиск при разных типах граничных условий


Здесь хочется просто дать всем совет:
- Внимательно читайте требования к фильтрам, обращая внимание на границы поиска, а именно: границы включены (=, >=, <=) или не включены (<, >).

Пример 2.1. Поиск места работы по дате:
Имеющийся фильтр: Имя, Фамилия, Дата работы (границы включены - =, >=, <=)

Тестовый пользователь:

ИмяИван
ФамилияИванов
Дата трудоустройства30/10/2007
Дата увольнения (последний рабочий день)30/10/2008
Место работыСмольный

В данном примере, помимо проверок из примера 1, Вам придется обратить внимание на тестирование фильтров по границам дат трудоустройства и увольнения.
Случаи тестирования:
Имя и ФамилияДата работыОписаниеОжидаемый результат
Иван, Иванов30/10/2007= левой границе,
< правой границы
Место работы Смольный
Иван, Иванов30/10/2008> левой границы,
= правой границе
Место работы Смольный
Иван, Иванов29/10/2007< левой границы,
< правой границы
Место работы не определено
Иван, Иванов31/10/2007> левой границы,
< правой границе
Место работы Смольный
Иван, Иванов31/10/2008> левой границы,
> правой границе
Место работы не определено


Пример 2.2. Поиск места работы по дате:
Имеющийся фильтр: Имя, Фамилия, Дата работы (границы НЕ включены - <, >)
Тестовый пользователь:

ИмяИван
ФамилияИванов
Дата трудоустройства30/10/2007
Дата увольнения (день, с которого сотрудник считается уволенным)30/10/2008
Место работыСмольный

Случаи тестирования:
Имя и ФамилияДата работыОписаниеОжидаемый результат
Иван, Иванов30/10/2007= левой границе,
< правой границы
Место работы Смольный
Иван, Иванов30/10/2008> левой границы,
= правой границе
Место работы не определено
Иван, Иванов29/10/2007< левой границы,
< правой границы
Место работы не определено
Иван, Иванов31/10/2007> левой границы,
< правой границе
Место работы Смольный
Иван, Иванов31/10/2008> левой границы,
> правой границе
Место работы не определено


Пример 3. Использование Wildcard символов


Наиболее распространенными wildcard символами являются "*" и "?".
* - включает любое число произвольных символов либо не включает ничего :)
? - включает ниодного или один произвольный символ

Тестовый пользователь: Иван, Иванов
Фильтр по фамилии с разрешенными Wildcard-ами
Случаи тестирования (*):
ФильтрРезультат
*ивановиванов, а также все фамилии заканчивающиеся на "иванов": криванов, ливанов и т.д
иванов*иванов, а также все фамилии начинающиеся на "иванов": иванович, иванова и т.д.
ив*овиванов, а также все фамилии начинающиеся на "ив" и оканчивающиеся на "ов": иваньков, ивченков и т.д.
*иванов*иванов, а также все фамилии имеющие в середине сочетание "иванов": диванович, иванович, диванов и т.д.

Случаи тестирования (?):
ФильтрРезультат
?ивановиванов, а также все фамилии заканчивающиеся на "иванов": диванов, ливанов и т.д
иванов?иванов, а также все фамилии начинающиеся на "иванов": ивановъ, иванова и т.д.
ив?новиванов, а также все фамилии начинающиеся на "ив" и оканчивающиеся на "нов": иванов, ивенов и т.д.
?иванов?иванов, а также все фамилии имеющие в середине сочетание "иванов": диванова, ивановъ, диванов и т.д.

Примеры с совместным использованием wildcard придумайте сами, не так это и сложно.

* * *

Это было всего лишь несколько рекомендаций, от которых можно отталкиваться при тестировании фильтров и поиска. Надеюсь, что они помогут Вам и сделают выпускаемые вами продукты более чистыми от багов :)

Напоследок расскажу, как мы автоматизированно тестировали правильность поиска на одном японском сайте:
1. Выкачали из базы все необходимые данные
2. Создали список кейвордов для поиска
3. Задали фильтры, используя заготовленные кейворды
4. Сверили наши данные с теми, что получили от приложения
5. Написали баги, если нашли :)

К выходу продукта в свет, ни одного открытого на фильтры бага не было...

Удачного тестирования,
Да пребудет с Вами Качество...

Читать далее...

Условия копирования публикаций:

Все публикации на данном блоге являются частной собственностью авторов. Любое копирование информации допускается только при условии указания имени автора и активной ссылки на источник.