воскресенье, 23 марта 2014 г.

Немного необходимого в Java.

Немного необходимого в Java.

Решил написать несколько теоретический пост,про основные понятия в Java,которые нужно знать каждому(по моему мнению).
Есть много вроде простых вопросов,на которые очень мало кто может ответить, еще их достаточно часто могут спросить и на собеседованиях)
В общем не будет лишним заметка.
Поехали:
Самый популярный вопрос:
Что такое Hashmap и как он работает?
Да все просто на самом деле.

Map - интерфейс, описывающий  классы, предназначенные для хранения пар объектов. Один из объектов пары должен быть уникальным  - называется "ключом" (key), второй объект, необязательно уникальный, называется "значением" (value). Всю пару называют "записью" (entry).

Уникальный - значит не равный другим ключам. Как определить "равность"? Достаточно знать, что ключи в Map сравниваются по методу equals(), и при попытке добавить в Map запись с не уникальным ключом предыдущая запись будет затерта новой.

Как HashMap устроен внутри?

Внутри HashMap - это массив. Элементы массива в документации называются бакетами (buckets).  В бакете хранится первый элемент связанного списка.Таким образом, HashMap внутри - массив связанных списков. Элемент связанного списка - объект класса Entry, содержит ключ, значение и ссылку на следующий Entry.

Что происходит при добавлении записи?

На следующем шаге, рассчитывается хеш-значение используя хеш-код ключа, получаемый вызовом метода hashCode(). Это хеш-значение используется для вычисления позиции в массиве, куда будет помещен объектEntry. Дизайнеры JDK предполагали, что плохо написанная функция hashCode() может вернуть слишком высокое или слишком низкое значение хеш-кода. Для решения этой проблемы, они ввели другую hash() функцию, и передали в нее значение хеш-кода объекта, чтобы привести хеш-значение в соответствие с размером массива.Теперь вызывается функция indexFor(hash, table.length), для вычисления точной позиции, куда будет помещен объект Entry
Далее, найдя нужный бакет, запись помещается в соответствующий связанный список.


А как искать элемент?

Поиск производится по ключу (ключ для поиска). Опять же по hashcode() ключа находим нужный бакет. Нашли бакет - значит нашли нужный нам связанный список. Далее проходим по списку и сравниваем ключи элементов списка с нашим ключом для поиска. Сравнение производим по функции equals(). Вернула функция true - значит нашли. 

И для чего все это нужно?
Для ускорения работы. В первую очередь ускорение поиска. Напомню, соответствие между hashcode ключа и бакетом не хранится, а вычисляется. Математические вычисления - это быстро. Далее, сравнивать объекты по equals() все же придется, но количество этих объектов ограничено.

Есть такие понятия как loadfactor и capacity-но они простые,даже писать не буду)
Как то много получилось про HashMap сейчас попробую короче укладываться.


В чем отличие между static synchronized и просто synchronized?

Использование слова synchronize на static-методе помечает весь класс как synchronize, т.е. такая запись: 
?
1
2
3
synchronized static void foo() {
    ...
}
равнозначна такой: 
?
1
2
3
4
5
static void foo() {
    synchronized(SomeClass.class) {
        ...
    }
}
А такая: 
?
1
2
3
synchronized void foo() {
    ...
}
такой: 
?
1
2
3
4
5
void foo() {
    synchronized(this) {
        ...
    }
}
Что значит synchronized(this)?

Семантически этот код: 
?
1
2
3
4
5
public void blah() {
  synchronized (this) {
    // do stuff
  }
}
эквивалентен этому: 
?
1
2
3
public synchronized void blah() {
  // do stuff
}
Однако synchronized(this) использовать не рекомендуется. Почему? Об этом можно почитать тут .



Что означают ключевые слова volatile и transient.

Определение переменной с ключевым словом volatile(«изменчивый») означает, что значение переменной будет изменяться несколькими потоками.

Переменная помеченая словом transient не будет сериализоваться и соответственно при десериализации её значение будет установлено значением по умолчанию.

Условия сериализации объектов?

Свойства класса должны быть сериализуемы и класс должен реализовывать маркирующий интерфейс Serializable. Не лишним будет вспомнить про serialVersionUID и его значение по умолчанию.

Можно ли передать объект в synchronized?

Да. Например очень популярна вот такая несложная конструкция: 
?
1
2
3
4
5
6
7
8
9
private final Object _SyncObject = new Object();
...
public void run() {
...
  synchronized(_SyncLock) {
    ...
  }
...
}
Работа со ссылками (weak и strong reference).

Об этом можно почитать тут.

Как сделать ArrayList синхронизированым в одну строку?
?
1
List<Record> s_list = Collections.synchronizedList(list);
Контракт между equals и hashCode.

Если equals возвращает true, то hashCode должен вернуть одно и то же значение. Обратное не верно.

Collections
Контейнеры бывают двух видов:
  1. Коллекции (List, Set, Queue)
  2. Ассоциативный массив (Map)
К популярным реализациям контейнеров стоит отнести: ArrayList, LinkedList, HashSet, TreeSet, LinkedHashSet, HashMap, TreeMap, LinkedHashMap.

В ArrayList и LinkedList элементы хранятся в порядке вставки. Различаются скоростью выполнения и количеством тех или иных операций: ArrayList имеет оптимизированый доступ к элементам списка, но обладает низкой производительностью при добавлении/удалении элементов в середину списка. LinkedList - наоборот.
HashSet обеспечивает наибольшую производительность при выборке элементов. TreeSet хранит элементы отсортированными по возрастанию в порядке сравнения. LinkedHashSet хранит элменты в порядке добавления.
HashMap обеспечивает наибольшую производительность. TreeMap хранит ключи отсортированными. LinkedHashMap хранит ключи в порядке вставки, но обеспечивает скорость HashMap.

Inner classes
Внутренний класс - класс, который объявлен внутри другого класса, например: 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Outer.java
public class Outer {
    Outer() {
        Inner i = new Inner();
        i.innerMethod();
    }
    // Inner class
    private class Inner {
        Inner() {}
        public void innerMethod() {
            // Do something.
        }
    }
}
Анонимный внутренний класс - это класс, который определен в методе другого класса и может быть использован только один раз - там, где он определен. Примером анонимного внутреннего класса часто является реализация обработчика какого-либо события.

Serialization
Сериализация это процесс сохранения состояния объекта в последовательность байт;десериализация это процесс восстановления объекта, из этих байт. Java Serialization API предоставляет стандартный механизм для создания сериализуемых объектов.
Сохранение объекта: 
?
1
2
3
4
5
6
7
8
9
FileOutputStream fos = new FileOutputStream("temp.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
TestSerial ts = new TestSerial();
oos.writeObject(ts);
oos.flush();
oos.close();
Восстановление объекта: 
?
1
2
3
FileInputStream fis = new FileInputStream("temp.out");
ObjectInputStream oin = new ObjectInputStream(fis);
TestSerial ts = (TestSerial) oin.readObject();

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

воскресенье, 26 января 2014 г.

Распараллеливание тестов WD+TestNG с использованием ThreadLocal

Распараллеливание тестов WD с использованием ThreadLocal

Итак,для тех кто не знает что такое ThreadLocal в Java

ThreadLocal — http://java.sun.com/javase/6/docs/api/java/lang/ThreadLocal.html
Незаслуженно забытый класс, существующий ещё с версии 1.2. И ставший ещё более привлекательным, подружившись с generics. Класс позволяет имея одну переменную, иметь различное значение для каждого из потоков.

То есть для тестов на WD он нам подходит идеально,чтобы не задумываться над синхронизацией между потоками и состоянии драйвера и других объектов.
Сейчас покажу то,что нам потребуется чтобы наши тесты выполнялись параллельно, каждый метод в отдельном браузере с независимым объектом WebDriver.
(каждый метод это конечно для примера,вообще на каждый метод выделять по запуску браузера-это роскошь)
Как его применить в проекте?
Примерно так:
Сделаем класс фабрику:
 
class LocalDriverFactory {
    static WebDriver createInstance(String browserName) {
        if (browserName.toLowerCase().contains("firefox")) {
            return new FirefoxDriver();
        }
        if (browserName.toLowerCase().contains("internet")) {
            return new InternetExplorerDriver();
        }
        if (browserName.toLowerCase().contains("chrome")) {
             return new ChromeDriver();
        }
        return new FirefoxDriver();
    }
}
Нам понадобится класс менеджер с ThreadLocal

 
public class LocalDriverManager {
    private static ThreadLocal webDriver = new ThreadLocal();
 
    public static WebDriver getDriver() {
        return webDriver.get();
    }
 
    static void setWebDriver(WebDriver driver) {
        webDriver.set(driver);
    }
}

Простой класс где мы сеттим в  ThreadLocal переменную WD и запрашиваем его.
Настало время переопределить TestNG Listener,для действий перед методом и после.

 
public class WebDriverListener implements IInvokedMethodListener {
 
    @Override
    public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {
        if (method.isTestMethod()) {
            String browserName = method.getTestMethod().getXmlTest().getLocalParameters().get("browserName");
            WebDriver driver = LocalDriverFactory.createInstance(browserName);
            LocalDriverManager.setWebDriver(driver);
        }
    }
 
    @Override
    public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
        if (method.isTestMethod()) {
            WebDriver driver = LocalDriverManager.getDriver();
            if (driver != null) {
                driver.quit();
            }
        }
    }
}
И напоследок,какой нибудь демо тест
В этом примере,у нас должны запуститься оба метода в разных потоках и с разными экземплярами WD.
Для проверки мы выводим эту информацию в консоль.

 
public class ThreadLocalDemo {
    @Test
    public void testMethod1() {
        invokeBrowser("http://www.ndtv.com");
    }
 
    @Test
    public void testMethod2() {
        invokeBrowser("http://www.facebook.com");
 
    }
 
    private void invokeBrowser(String url) {
        System.out.println("Thread id = " + Thread.currentThread().getId());
        System.out.println("Hashcode of webDriver instance = " + LocalDriverManager.getDriver().hashCode());
        LocalDriverManager.getDriver().get(url);
 
    }
}

И xml suite для запуска

 






    
        
        
            
        
     
 


 
[TestNG] Running:
Thread id = 10
Hashcode of webDriver instance = 1921042184
Thread id = 9
Hashcode of webDriver instance = 2017986718
 
===============================================
Suite
Total tests run: 2, Failures: 0, Skips: 0
===============================================

Вот и все.

понедельник, 16 декабря 2013 г.

Тестирование верстки 2

Тестирование с помощью fighting layout bugs

Я думаю не многие знают про гугловую open-source либу,для тестирования верстки и различных layouts,а она совсепм даже не плоха.
Взять ее можно вот тут.
Если кратко,что она умеет?
Находит битые ссылки:
  • Сканирует  HTML теги <img> и проверяет src атрибуты.
  • Сканирует CSS  вск атрибуты style и <style> елементы в HTML и все указанные URL в них.
  • Проверяет фавиконки по урлам.
Проверяет необходимый горизонтальный скроллинг

 Можно указать минимально поддерживаемое разрешение экрана для вашей веб страницы
примерно так:


 
FightingLayoutBugs flb = new FightingLayoutBugs(); flb.configure(DetectNeedsHorizontalScrolling.class).setMinimalSupportedScreenResolution(800, 600);

По умолчанию минимальное разрешение 1024 x 768.

  • Так же проверяет текст на слишком маленький контраст.
  • Распознает текст, который очень близко или перекрывает горизонтальный край
  • Распознает текст, который очень близко или перекрывает вертикальный край
И собственно прикрутить,ее очень просто

 
    FirefoxDriver driver = new FirefoxDriver();
    try {
        String testPageUrl = "http://www.test.de/";
        driver.get(testPageUrl);
        WebPage webPage = new WebPage(driver);
        FightingLayoutBugs flb = new FightingLayoutBugs();
        final Collection layoutBugs = flb.findLayoutBugsIn(webPage);
        System.out.println("Found " + layoutBugs.size() + " layout bug(s).");
        for (LayoutBug bug : layoutBugs) {
            System.out.println(bug);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        driver.quit();
    }


 В общем я думаю стоит попробовать.

четверг, 5 декабря 2013 г.

Тестирование верстки

Тестирование верстки

В общем то идея далеко не новая,как быстро и автоматизированно следить за тем,чтобы при новом билде не расползлась верстка в разных браузерах.
Браузеров много,проект большой,мануальные тестировщики все не успеют.
Попробуем автоматизировать.
Для этого подойдет Selenium WD(т.к. у него поддержка кучи браузеров и снятие скриншотов,то которое нам нужно) и всего навсего средства Java чтобы следить за соответствием скринов.
Не буду говорить, как именно нужно снимать и проверять скриншоты,но мне кажется удобно или сделать пачку "эталонных скринов" и новые сравнивать с ними,или лезть на боевой сервер,делать скрин оттуда,а после снимать скрин на qa-стенде.
Второй вариант дольше,но надежнее в плане не устаревани "эталонных скриншотов".
В общем хватит об этом,как это сделать?
Примерно так:
Через WD  снять скрин можно так:

 
   public void makeScreenshot(String methodName) {
        try {
            File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
            FileUtils.copyFile(scrFile, new File("target" + File.separator + "failure_screenshots" +
                    File.separator + methodName+".png"));
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    } 
Конечно это не единственный способ для снятия скриншотов,но сойдет.
Вроде есть много java-библиотек для работы с изображениями,но стоящую и для такой не трудной задачи искать действительно тяжело.(я вроде как не нашел)
в общем можно использовать этот класс:


import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * I am gonna pass two images and I am gonna recort only the differences
 * trying to catch if there is a different object or not int the scene
 *
 * @author maikon
 */
public class CheckingDifferentImages {

    public static void checkDifference(String pathToTheFirstScreen, String pathToTheSecond, String nameDifference) {
        BufferedImage im1 = null;
        BufferedImage im2 = null;
        try {
            //loading the two pictures
            //read and load the image
            BufferedImage input = ImageIO.read(new File("target"+File.separator+"failure_screenshots"+File.separator+pathToTheFirstScreen));
            //build an image with the same dimension of the file read
            im1 =
                    new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_INT_ARGB);
            //object create to draw into the bufferedImage
            Graphics2D g2d = im1.createGraphics();
            //draw input into im
            g2d.drawImage(input, 0, 0, null);
            //making all again for the second image

            BufferedImage input2 = ImageIO.read(new File("target"+File.separator+"failure_screenshots"+File.separator+pathToTheSecond));
            //build an image with the same dimension of the file read
            im2 =
                    new BufferedImage(input2.getWidth(), input2.getHeight(), BufferedImage.TYPE_INT_ARGB);
            //object create to draw into the bufferedImage
            Graphics2D g2d2 = im2.createGraphics();
            //draw input into im
            g2d2.drawImage(input2, 0, 0, null);
        } catch (IOException ex) {
            Logger.getLogger(CheckingDifferentImages.class.getName()).log(Level.SEVERE, null, ex);
        }

        showDifference(im1, im2, nameDifference);

    }

    public static void showDifference(BufferedImage im1, BufferedImage im2, String nameDifference) {
        BufferedImage resultImage =
                new BufferedImage(im1.getWidth(), im2.getHeight(), BufferedImage.TYPE_INT_ARGB);

        double THR = 50;
        int area = 0;
        for (int h = 0; h < im1.getHeight(); h++) {
            for (int w = 0; w < im1.getWidth(); w++) {

                int red1 = 0xff & (im1.getRGB(w, h) >> 16);
                int green1 = 0xff & (im1.getRGB(w, h) >> 8);
                int blue1 = 0xff & im1.getRGB(w, h);


                int red2 = 0xff & (im2.getRGB(w, h) >> 16);
                int green2 = 0xff & (im2.getRGB(w, h) >> 8);
                int blue2 = 0xff & im2.getRGB(w, h);

                //euclidian distance to estimate the simil.
                double dist = 0;
                dist = Math.sqrt(Math.pow((double) (red1 - red2), 2.0)
                        + Math.pow((double) (green1 - green2), 2.0)
                        + Math.pow((double) (blue1 - blue2), 2.0));
                if (dist > THR) {
                    resultImage.setRGB(w, h, im2.getRGB(w, h));
                    area++;
                } else {
                    resultImage.setRGB(w, h, 0);
                }
           //2nd option
           /*     if (dist > THR) {
                    resultImage.setRGB(w, h,255);
                    area++;
                } else {
                    resultImage.setRGB(w, h, im1.getRGB(w, h));
                }*/
            } //w
        } //h
        try {
            File fileScreenshot = new File("target" + File.separator + "DifferenceScreens" + File.separator + nameDifference);
            fileScreenshot.getParentFile().mkdirs();
            ImageIO.write(resultImage, "PNG", fileScreenshot);
        } catch (IOException ex) {
            Logger.getLogger(CheckingDifferentImages.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    //end functionn

}

Попробуем что нить простое(не придираться,это для примера)

 
  @Test
    public void testYandex(){
        get("http://www.yandex.ru/");
        makeScreenshot("1");
        get("http://www.yandex.ru/");
        makeScreenshot("2");
        CheckingDifferentImages.checkDifference("1.png","2.png","diff.png");
    }

В результате:
1й скрин
2й скрин
и отличие(если вы заметили то в классе 2 варианта,один закомменитрован) так что:
и 2м способом

Вот и все.

среда, 4 декабря 2013 г.

Security Regression Tests

 Тестированиe безопасности в регрессе

В общем у многих есть регресс сьюты,но мало у кого есть какое-либо тестирование безопасности,я попробую рассказать ,как его можно добавить без особых затрат.
И так,подразумеваем,что у нас есть рабочий регресс который покрывает большую(а лучше полную) часть нашего функционала веб-приложения.
Значит нужно к нему привязать хороший сканер уязвимостей,который поможет находить проблемы на более ранних стадиях разработки.
Советую обратить внимание на инструмент OWASP ZAP, потому что он (как мне кажется) полностью удовлетворяет нашим требованиям.(ну и + OWASP классная команда с активным комьюнити и все такое)
В общем  для начала о нем,что он из себя представляет.Можно погуглить и посмотреть на эти слайды:



А теперь как это работает у меня

Есть сборщик Maven который собирает и запускает наши тесты, и поднимает тулзу Owasp ZAP,которая поднимает свой прокси который я ему задал.
Затем,мы поднимаем браузер с настроенным прокси(указываем прокси ZAP),в котором будут выполняться тесты.
Это позволит нам видеть все запросы и ответы производимые тестами и так же позволит нам отслеживать и проверять все AJAX запросы (которые не всегда могут быть протестированы).
ZAP пассивно просматривает все ответы от веб-приложения и проводит проверку по заданным "рулам".
Пассивное сканирование не изменяет ответы и запросы и следовательно безопасно в использовании на любых площадках.
Сканирование выполняется в фоновом потоке, что исключает снижение скорости работы приложения.

После прогона всего сьюта можно пробежаться пауком по заданному узлу,чтобы найти то,что не прошло через наше прокси.
После чего запускаем активное скнирование
Активное сканирование пытается найти потенциальные уязвимости с использованием известных OWASP аттак.(а их совсем не мало)
Не желательно использовать такой вид тестирования на боевом сервере.
Естественно, активное сканирование находит только определенные виды уязвимостией,логические уязвимости,как контроль доступа, проверены быть не могут и должны проверятся мануальным тестированием.
И да,проводится также проверка на CSRF форм (это проверка на сабмит форм со случайным параметром, используемых для защиты от CSRF атак.)

Вот и все,всем спасибо.