Страницы

Поиск по вопросам

Показаны сообщения с ярлыком junit. Показать все сообщения
Показаны сообщения с ярлыком junit. Показать все сообщения

воскресенье, 15 марта 2020 г.

Как сделать data-driven юнит-тесты?

#java #юнит_тесты #junit

Тестирую веб-сайт с использованием Selenium Webdriver (Firefox) и JUnit. В данный
момент разные тест-кейсы работают с одним набором данных. Хочу разделить данные и реализацию,
чтобы запускать одни и те же сценарии с разными данными.

Пока что данные инциализируются в @Before, но я могу выделить их хоть в XML, хоть
в Properties. С этим затруднений нет.

В чем проблема: не представляю, как заставить юнит-тесты запускаться многократно,
используя различные данные?

Я мог бы легко это сделать, отказавшись от использования JUnit. Просто вручную запускать
методы и вручную же перебирать и скармливать им данные. Но это страшный велосипед и
я не хочу терять преимуществ JUnit (таких, например, как автозапуск через Maven, красивое
логирование, легкий запуск одного метода из тест-комплекта либо всего комплекта и т.д).

public class TestSuite {
    /**
     * Входные данные — строки и числа 
     * */
    String searchString;
    String firmID;
    String geoID;

    private DGDriver driver; //extends FirefoxDriver

    @Before
    public void setUp() throws Exception {
        driver = new DGDriver(); 
        driver.manage().timeouts().implicitlyWait(1000, TimeUnit.SECONDS);

        /** Сейчас данные инициализируются так */
        searchString = "главный вокзал";
        firmID = "141265769369926";
        geoID = "141373143526113";

    }

    @Test
    public void testCase1 {

        /* Тут вызываются методы, использующие данные.
        Думаю, по именам понятно, как они их используют */

        driver.homepage();
        driver.searchFor(searchString);
        driver.searchResults.clickItem(firmID);
        driver.firmCard.clickAddress();
        ...
    }
    ...
}

    


Ответы

Ответ 1



Решил следующим образом: В JUnit есть аннотации @Parametrized /** * Обязательна вот такая аннотация класса: */ @RunWith(Parameterized.class) public class LeafletMarkerTests { /** * значение аннотации value указывает на номер параметра в массиве */ @Parameterized.Parameter(0) public String searchString; @Parameterized.Parameter(1) public String firmID; @Parameterized.Parameter(2) public String geoID; @Parameterized.Parameter(3) public int expectedTransformX; @Parameterized.Parameter(4) public int expectedTransformY; @Parameterized.Parameter(5) public int expectedTransformZ; Vector3d expectedCzarTransform; // разные другие переменные /** * можно задать названия датасетов, чтобы было понятнее, на каком тест зафейлился */ @Parameterized.Parameters(name = "{index}: {0}") public static Collection data() { return Arrays.asList(new Object[][]{ /*{searchString, firmID, geoID, x, y, z}*/ {"главный вокзал", "141265769369926", "141373143526113", 767, 289, 0}, {"цирк", "141265769338191", "141373143518884", 935, 289, 0}, {"оперный", "141265769360673", "141373143521691", 767, 289, 0}, {"старый дом", "141265769360664", "141373143532548", 767, 289, 0}, {"сансити", "141265770417218", "141373143572328", 767, 289, 0}, }); } @Before public void setUp() throws Exception { driver = new DGDriver(); //extends FirefoxDriver driver.manage().timeouts().implicitlyWait(1000, TimeUnit.SECONDS); // Инициализация этих параметров в @Before уже не нужна } public void test1() {...} ... } Так запущенный параметризованный тест-комплект выглядит в IntelliJ IDEA CE: Нашел специализированное решение для data-driven тестов на JUnit.

пятница, 13 марта 2020 г.

Как вы истрактовали бы постановку тестовой задачи?

#java #юнит_тесты #тестирование #junit

Имеется тестовое задание:


  Дан файл вида

operand1;operand2;operation;result
operand1;operand2;operation;result
operand1;operand2;operation;result
operand1;operand2;operation;result

  
  
  
  Каждая строка описывает арифметическое действие. 
  
  
  operand1 и operand2 - операнды, целые числа 
  operation - арифметическое действие + - / * 
  result - результат операции operation над operand1 и operand2
  
  
  В файле могут содержаться любые значения полей
  
  
  
  Требуется
  
  
  Реализовать юнит (JUnit) тесты арифметических действий.
  Каждое действие должно выглядеть в отчете как отдельный тестовый сценарий
  
  
  Конец задания
  
  


Я понимаю, как написать тесты JUnit, но не понимаю, что именно тестируется в данном
случае.

Правильно ли я считаю, что нужно сначала написать код, который парсит переменные,
4 функции для разных арифметических действий, а потом проверять с помощью JUnit корректность
работы функций на основании тестовых равенств в файле 
?

Прошу вас высказать свое понимание задачи.
    


Ответы

Ответ 1



Я бы написал скрипт (хотя, если там несколько строк, то можно и ручками), который на основании исходного файла нагенерирует Java файл с тестами. То есть, на каждую строку исходного файла будет генерировать что то вида @Test public void test1() { int actual = operand1 operationoperand2; int expect = result; assertEquals(expect , actual); } ну м конечно несколько строк "обвязки" для всего этого, что бы модуль был "компилируемый". В более навороченном виде я бы добалял проверку на 0 для operand2 если operation равно /. UPD Вот на коленке на perl сделать за минут 5, для собеседования считаю самое оно #!/usr/bin/perl use strict; use warnings; print ' import junit.framework.*; public class JavaTest extends TestCase { protected void setUp(){ } '; my $i = 1; while (my $line = <>) { chomp $line; my ($op1, $op2, $oper, $result) = split /;/, $line; print <<"ONE_TEST"; \@Test public void test$i() { int actual = $op1 $oper $op2; int expect = $result; assertEquals(expect, actual); } ONE_TEST $i += 1; } print "}\n";

Ответ 2



Я бы сделал 4 теста по 1 на каждую операцию (+ - / *) Каждый тест бы выбирал из файла строки со своими операциями и проверял правильность результата. Но конечно задача очень странная.

четверг, 13 февраля 2020 г.

Как исправить java.lang.NullPointerException?

#java #junit

Когда запускаю тест выдает ошибку java.lang.NullPointerException в s1.setId(1). Как
это исправить?

Весь код:

public class UserTest extends TestCase{
    private User user;
    private Series s1;

    public void setUp(){
        user = new User(1, "Dan", 4, 300);

        s1.setId(1);
        s1.setTitle("Mr. Robot");
        s1.setStatus(1);
        s1.setActors("Rami Malek, Christian Slater, Portia Doubleday");
        s1.setEpisodesCounts(10);
        s1.setRating(9.1);
    }

    public void test1(){
        User.getUserId(user);
    }

    public void test2(){
        user.loadUserSeriesLib();
    }

    public void test4(){
        user.addNewSeriesToLib(s1);
    }
}

    


Ответы

Ответ 1



private Series s1; в данной строке проведедено лишь объявление ссылки. но никакой инициализации объекта нет, т.е. s1 не знает куда ссылаться. чтобы это исправить нужно проинициализировать поле s1: s1 = new Series();

Ответ 2



Создать объект типа Series и присвоить ссылку на него полю класса s1: public void setUp(){ user = new User(1, "Dan", 4, 300); s1 = new Series(); s1.setId(1); ... }

Как избежать дублирования в юнит-тестах?

#java #юнит_тесты #junit

Тестирую некоторый алгоритм. Данные для алгоритма хранятся в списке. В итоге у меня
получается вот так:

@Test
public void testOneDirectModeBestCaseEven(){
    list.add(new Agent(6));
    list.add(new Agent(1));
    list.add(new Agent(2));
    list.add(new Agent(3));
    list.add(new Agent(4));
    list.add(new Agent(5));

    int i = 0;
    while(!list.hasSolution()){
        LeaderElection.solve(list, i++);
    }

    int leaderId = list.getLeaderId();
    assertEquals(6, leaderId);
}

@Test
public void testOneDirectModeBestCaseOdd(){
    list.add(new Agent(5));
    list.add(new Agent(1));
    list.add(new Agent(2));
    list.add(new Agent(3));
    list.add(new Agent(4));

    int i = 0;
    while(!list.hasSolution()){
        LeaderElection.solve(list, i++);
    }

    int leaderId = list.getLeaderId();
    assertEquals(5, leaderId);
}


И еще много функций. Получается дублирование в том что я заполняю List в каждом тесте.
Но и в setUp не вынесешь, потому что заполнять то нужно по-разному для каждого теста. 
    


Ответы

Ответ 1



Вам могут помочь параметризованные тесты. JUnit их тоже поддерживает. Приведу код, подробнее почитать можете по ссылкам. (На джаве давно не писал, поэтому скорее всего тут есть ошибки компиляции :)) @RunWith(Parameterized.class) public class LeaderElectionTests{ @Parameters public static Collection data(){ return Arrays.asList(new Object[][]{ { <список 1>, <ответ 1> }, { <список 2>, <ответ 2> } }); } private List list; private int expected; public LeaderElectionTests(List list, int expected){ list = input; expected = expected; } @Test public void testOneDirectModeBestCase(){ int i = 0; while(!list.hasSolution()){ LeaderElection.solve(list, i++); } int leaderId = list.getLeaderId(); assertEquals(answer, leaderId); } } Однако такой подход нужно использовать только когда вы тестируете один кейс на разных наборах входных данных. Если же у вас разные кейсы, причем эта разница заложена непосредственно во входных данных, то я бы рекомендовал идти по пути отдельных тестов. Это проще для восприятия, потому что так или иначе в названия тестов мы закладываем наши решения и наш опыт, полученные в процессе написания кода. Глядя же на обезличенный список тестовых наборов данных, через месяц уже будет сложно сказать, чем они отличаются между собой. Судя по коду, ваш объект list -- не просто список, он еще содержит в себе некоторую логику. Так что скорее всего разумнее будет остаться с разными тестами и дублирующимися данными, которые на самом деле не дублирующие данные, а разные тест кейсы. Хотя вам должно быть виднее, конечно.

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

Как протестировать скорость работы и затраты памяти?

#юнит_тесты #java #junit

Я начинаю осваивать Java, так что без рук). 
Мне нужно на простой программе протестировать скорость роботы и затраты памяти. 
Как в Java можно это сделать?

import java.io.*;
class SearchPhrase {

// walk to root way
public void walk(String path, String whatFind) throws IOException {

    File root = new File(path);
    File[] list = root.listFiles();
    for (File titleName : list) {
        if (titleName.isDirectory()) {
            walk(titleName.getAbsolutePath(), whatFind);
        } else {
            if (read(titleName.getAbsolutePath()).contains(whatFind)) {
                System.out.println("File: " + titleName.getAbsolutePath());
            }
        }
    }
}

// Read file as one line
public static String read(String fileName) {
    StringBuilder strBuider = new StringBuilder();
    try {
        BufferedReader in = new BufferedReader(new FileReader(new File(
                fileName)));
        String strInput;
        while ((strInput = in.readLine()) != null) {
            strBuider.append(strInput);
            strBuider.append("\n");
        }
        in.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return strBuider.toString();
}

public static void main(String[] args) {

    SearchPhrase example = new SearchPhrase();

    try {
        example.walk("C:\\Documents and Settings\\User\\Java", "programmed");
    } catch (IOException e) {
        e.printStackTrace();
    }
  }
}

    


Ответы

Ответ 1



Это решение разработано самим автором вопроса, но он оставил его в вопросе. Время работы программы: public static void main(String[] args) { SearchPhrase example = new SearchPhrase(); long startTime = System.currentTimeMillis(); try { example.walk("E:\\Document\\Effortless English\\Level 3", "spot"); } catch (IOException e) { e.printStackTrace(); } long stopTime = System.currentTimeMillis(); long elapsedTime = stopTime - startTime; System.out.println(elapsedTime); } Потребление памяти: package task; public class SearchPhrase_PerformanceTest { private static final long MEGABYTE = 1024L * 1024L; public static long bytesToMegabytes(long bytes) { return bytes / MEGABYTE; } public static void main(String[] args) { SearchPhrase example = new SearchPhrase(); example.askUserPathAndWord(); // Get the Java runtime Runtime runtime = Runtime.getRuntime(); // Run the garbage collector runtime.gc(); // Calculate the used memory long memory = runtime.totalMemory() - runtime.freeMemory(); System.out.println("Used memory is bytes: " + memory); System.out.println("Used memory is megabytes: " + bytesToMegabytes(memory)); } } Все просто и понятно ;)

четверг, 23 января 2020 г.

Синглтон и тестирование

#java #юнит_тесты #junit

В общем есть следующий код, состоящий из синглтона и класса, использующего его (упрощенная
версия).

public final class Singleton {
    private static Singleton s_instance = new Singleton();

    public static Singleton getInstance(){
        return s_instance;
    }

    public String getString(){
        return "string";
    }

    public String getStringTest(){
        return "string_test";
    }
}


public class SomeClass{
    private String someString;

    public SomeClass(){
        someString = Singleton.getInstance().getString();
    }
}


Синглтон считывает конфигурацию из файла и предоставляет методы для ее получения
компонентами программы (getString() и т.п.). 

Для тестирования работы класса SomeClass мне нужно, чтобы он получил другое значение
при вызове getString(), а именно то, которое вернет getStringTest().

Я хотел бы иметь что-то типа такого, но столкнулся с тем, что не знаю, как создать
mock или spy объект для синглтона:

when(Singleton.getInstance().getString()).thenReturn(Singleton.getInstance().getStringlTest());


Ну и как следствие вопрос: Как можно создать mock или spy объект для синглтона или
как по-другому можно подменить возвращаемое синглтоном значение? Java, jUnit4.
    


Ответы

Ответ 1



Mockito, мне кажется, прекрасно справится с Вашей задачей. Singleton singleton = mock(Singleton.class); when(singleton.getStringTest()).thenReturn("What you need");

Ответ 2



Приведу самый эффективный, пусть и не очень красивый (и очень непопулярный) вариант тестирования Singleton. Шаг 1. Добавьте метод Singleton.setInstance и сделайте конструктор Singleton защищенным: public final class Singleton { private static Singleton s_instance = new Singleton(); public static setInstance(Singleton instance) { s_instance = instance; } // ... } Шаг 2. Мокните класс Singleton любым способом и задайте полученный объект через Singleton.setInstace Шаг 3. Протестируйте класс SomeClass. А когда у вас появится время для выплаты технического долга, выполните еще один шаг: Шаг 4. Проведите рефакториг класса SomeClass, выкинув любое использование метода Singleton.getInstance. Инжектируйте экземпляр Singleton в SomeClass в явном виде.

Чем хорош паттерн Arrange, Act, Assert и стоит ли им пользоваться всегда?

#java #юнит_тесты #junit

Я вижу в этом паттерне только один плюс: он улучшает читаемость. 
А минусов больше:

Во-первых, это далеко не всегда улучшает читаемость. Вот простой тест:

@Test
public void testAdditionAndSize() {
    int expectedSize = 3;
    int actualSize = list.size();
    assertEquals(expectedSize, actualSize);
}


Вместо этого я мог бы написать так:

@Test
public void testAdditionAndSize() {
    assertEquals(3, list.size());
}


Разве пострадала читаемость?

Во-вторых, что делать вот с таким тестом?

@Test
public void testIsEmpty() {
    assertTrue(!list.isEmpty());
}


Неужели тоже создавать 2 булевы переменные, а потом сравнивать их?

@Test
public void testIsEmpty() {
    boolean res = false;
    boolean actual = list.isEmpty();
    assertEquals(res, actual);
}


И в-третьих, На мой взгляд, этот паттерн усложняет написание некоторых тестов. Например:

@Test
public void testCicleNext() {
    assertEquals(8, list.next().getId());
    assertEquals(7, list.next().getId());
    assertEquals(9, list.next().getId());
    assertFalse(list.hasNext());
    assertEquals(8, list.next().getId());
    assertTrue(list.hasNext());
}


Тут перед каждым ассертом придется создавать по 2 переменные. В результате тест разрастется
до огромных размеров и читать его по-моему станет только тяжелее.
    


Ответы

Ответ 1



В любом паттерне всегда можно найти такой случай, когда он выглядит нелепо. Я думаю, что Вы как раз и привели такой пример. Классические обоснования паттерна 3А (да, так он пишеться - три А) следующие: код разделяется на три логические части. функция тестирования четко тестирует две вещи, без побочных эффектов. разберем первый тест: assertEquals(3, list.size()); Здесь не всегда понятно, есть ли у функции size побочные эффекты (да, в этом случае это звучит глупо, но в общем случае - нет). И допустим этот тест захотелось закомментировать временно. А код в тесте ниже может неявно зависеть от побочных эффектов. И все, прилетели... (да, в этом конкретном тесте одна строка, но кто его знает) Во-вторых, что делать вот с таким тестом? @Test public void testIsEmpty() { assertTrue(!list.isEmpty()); } А этот тест просто нужно правильно написать. Где то так @Test public void testListIsNotEmpty() { bool is_empty = list.isEmpty(); assertFalse(is_empty); } Видите разницу? нет переменных типа res, значение которых не понятно. Также, если список не должен быть пуст, то это и нужно тестить, а не противоположность (видите, я даже имя теста поменял). А теперь посмотрим на последний тест @Test public void testCicleNext() { assertEquals(8, list.next().getId()); assertEquals(7, list.next().getId()); assertEquals(9, list.next().getId()); assertFalse(list.hasNext()); assertEquals(8, list.next().getId()); assertTrue(list.hasNext()); } В этом примере next модифицирует исходный объект. И если одну строку закомментировать, то все, тест может порушиться. Этот тест кстати можно переписать через цикл и сразу он стает красивее и проще. И не нужно создавать кучу переменных. @Test public void testCicleNext() { int test_data[] = {8,7,9,8}; for (int i = 0; i < test_data.length(); i++) { int expect = test_data[i]; int actual = list.next().getId(); bool has_next = list.hasNext(); assertEquals(expect, actual); assertTrue(has_next ); } } Теперь, если нужно добавить ещё 5-6 тестируемых значений, то это очень просто. Мое личное мнение. Внутри тестируемых функций (то есть, assertEquals и подобных) не должно быть сложных конструкций. На то они и юнит тесты, что бы тестировать минимальную единицу кода. И если size еще как то допустим, то методы, которые модифицируют объект - нет. Если у меня возникает ситуация, что мне нужно продебажить функцию/метод, который я тестирую, то это первый звонок, что функция сложная и ее нужно разбить или переписать. Если тест стает очень большим (а Вы на это жалуетесь), значит тест написан неверно - его нужно либо разбить на два поменьше, либо написать правильно (я, к примеру в плюсах люблю для этого делать макросы:) ). Но в любом случае не нужно страдать паттернами головного мозга и применять их везде и всюду. Нужно применять их по надобности. я давно не писал на Java, поэтому в коде могут быть мелкие опечатки.

Ответ 2



Arrange-Act-Assert - это шаблон для форматирования Unit тестов. Обозначающий разделения теста на 3 части Arrange - все необходимые подготовки и входные данные Act - собственно вызов того метода который вы тестируете Assert - проверки, что метод делает то что надо В ваших пример со списком должно быть что-то такое: @Test public void testAdditionAndSize() { // Arrange - подготавливаем данные для теста SomeClass obj = new SomeClass; // Act - выполняем метод add list.add(obj); // Assert - проверяем, что объект добавился assertEquals(1, list.size()); assertEquals(obj, list.get(1)); // другие тесты проверяющие что добавился правильный объект, что он не изменился и т.д. }

среда, 22 января 2020 г.

Как ограничить доступ в интернет отдельному классу ?

#java #junit

При написании тестов на jUnit 4 столкнулся с необходимостью сымитировать потерю доступа
в интернет для проверки устойчивости приложения. Есть какие-нибуть стандартные механизмы,
которые это могут обеспечить ибо отключать WiFi при запуске тестов некошерно же. 
    


Ответы

Ответ 1



Все верно - ваш тест не должен зависеть от конфигурации и быть "полу" автоматическим. Скорее всего у вас сильно связанный код. Разделите на два метода - нормальное выполнение, и аварийное(скорее всего, метод с выбросом Exception). Тогда их можно будет проверить отдельно. Был бы код, объяснил бы с примером :)

Ответ 2



Вынесите кусок кода отвечающий за коннект в отдельный класс расширяющий стандартный функционал, например для сокетов: public TestSocket extends Socket { private Class myClass; public TestSocket(Class myClass) { super(); this.myClass=myClass; } @Override public InpuStream getInputStream() { if(myClass.getName().indexOf("MyTestSuite") > 0) { //если это тестовый класс return new ByteArrayInputStream(new byte[0])); //возвращаем пустой поток return super.getInputStream(); } } } Ну в общем схема как идея - дальше применительно к вашему коннекту можно развить и дальше.

воскресенье, 12 января 2020 г.

Запуск тестов в разных браузерах одновременно

#selenium #junit #selenium_webdriver

Как запустить один и тот же тест в нескольких браузерах одновременно? 

Пытался использовать Selenium grid, но не хватило знаний и навыков гугления. 

Запускаю на своей машине Windows 8.1
Использую JUnit Webdriver 2.0 maven
    


Ответы

Ответ 1



Параллельный запуск тестов является одним из мощных средств для ускорения тестирования. Хорошо автоматизированные тесты должны быть независимыми, изолированными и воспроизводимыми, эти качества делают их идеальными для одновременного выполнения. Однако на практике не все тестовые классы разработаны с возможностью параллельного запуска. Такие аспекты, как общие изменяемые переменные, общий доступ к файлу и базе данных, или использование встроенного веб-сервера, могут сделать параллельный запуск тестов очень сложным или вообще невозможным. Тем не менее, одновременный запуск тестов, определенно, очень полезная вещь. Начиная с версии 4.7 в JUnit была добавлена возможность параллельного запуска, для этого нужно настроить Maven следующим образом: [...] org.apache.maven.plugins maven-surefire-plugin 2.12.3 methods [...] Атрибут parallel может принимать значения «classes», «methods» или «both». При этом нельзя однозначно утверждать о количестве запущенных одновременно тестов, это напрямую зависит от параметров компьютера и настроек плагина по-умолчанию. Во время запуска теста найдите следующую строку в консоли, она позволяет узнать параметры с которыми выполняется параллельный запуск: ------------------------------------------------------- T E S T S ------------------------------------------------------- Concurrency config is parallel='methods', perCoreThreadCount=true, threadCount=2, useUnlimitedThreads=false Атрибут threadCount позволяет указать, сколько потоков должно быть выделено для запуска тестов (сколько тестов должно запускаться параллельно). Обратите внимание, что его использование с параметром perCoreThreadCount, установленным в true, может исказить реальное количество запускаемых одновременно тестов. В то же время perCoreThreadCount позволяет добиться большей гибкости при запуске тестов на разных машинах. Например, при запуске тестов со следующей конфигурацией на машине с 2-х ядерным процессором, одновременно будут выполняться 4 тестовых класса, а не 2: [...] org.apache.maven.plugins maven-surefire-plugin 2.12.3 classes true 2 [...] Существует еще такой атрибут как useUnlimitedThreads. При его использовании будет создаваться столько потоков, сколько классов или методов в Вашем проекте, и все тесты будут пытаться запуститься одновременно. useUnlimitedThreads отлично работает для юнит-тестов, но для функционального web тестирования его лучше не использовать. Настройки конфигурации полностью зависят от характера Ваших тестов, поэтому стоит поэкспериментировать с различными конфигурациями и посмотреть, какой из вариантов настройки больше всего подходит для Вас. Информация взята с Блога вебразработчика В будущем советую все таки использовать Google. И не бояться эксперементировать со своим проектом. Надеюсь предоставленная информация Вам поможет, удачи в дальнейших трудах)

суббота, 11 января 2020 г.

Как Assert.assertThat() в Junit делает сравнение результатов теста?

#java #junit

У меня тест в конце которого проверка двух объектов на равенство:

assertThat(result, is(proposal1));


И мне падает ошибка хотя Expected и Actual в логе показаны одинаковыми:

java.lang.AssertionError: 
Expected: is 
     but: was 
Expected :is 

Actual   :


Разве два объекта с одинаковым состояние не должны проходить assertThat???

Как мне это поправить?
    


Ответы

Ответ 1



Для того, чтобы объекты корректно сравнивались на равенство нужно переопределить метод Object.equals и реализовать логику сравнения по нужным полям. Если equals не переопределен, то будет использоваться реализация по умолчанию в который равными считаются только ссылки на один и тот же объект. Совпадение значений полей роли не играет. Из документации: The equals method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x and y, this method returns true if and only if x and y refer to the same object (x == y has the value true). Если equals переопределен, то нужно искать ошибку в реализации метода. UPDATE (по JUnit): Junit также не выполняет сверку полей в данном случае: assertThat обращается к переданному matcher-у, в данном случае is Asserts that actual satisfies the condition specified by matcher. is обращается к equalTo: A shortcut to the frequently used is(equalTo(x)). equalTo использует equals с предпроверкой на null: Creates a matcher that matches when the examined object is logically equal to the specified operand, as determined by calling the Object.equals(java.lang.Object) method on the examined object.

вторник, 31 декабря 2019 г.

Как в Mockito проверить что метод не был вызван?

#java #junit #mockito

Для того чтобы проверить что метод был вызван мы вызываем verify(mock).method().
А как проверить обратное, что метод вызван не был?
    


Ответы

Ответ 1



Использование метода verify в Mockito (из документации): LinkedList mockedList = mock(LinkedList.class); mockedList.add("once"); mockedList.add("twice"); mockedList.add("twice"); mockedList.add("three times"); mockedList.add("three times"); mockedList.add("three times"); Чтобы удостоверится что метод был вызван один раз verify(mockedList).add("once"); или verify(mockedList, times(1)).add("once"); Оба варианта идентичны так как: public static T verify(T mock) { return MOCKITO_CORE.verify(mock, times(1)); } Для вашего случая подойдут варианты: verify(mockedList, times(0)).add("never happened"); verify(mockedList, never()).add("never happened"); javajunitmockito

суббота, 28 декабря 2019 г.

Как запускать серию тестов JUnit строго последовательно?

#java #юнит_тесты #junit

Есть серия тестов. Я хочу, чтобы они использовали некоторый разделяемый объект. Первый
тест его вычисляет, второй (и последующие) требуют его для своей работы. Похоже, что
по умолчанию тесты запускаются в раздельных потоках и первый тест не успевает выполниться
к началу второго.

Про @Before знаю, он плохо подходит, хотелось бы точный контроль за выполнением.

Вопрос: как явным образом указать, что все тесты выполняются последовательно?

/** shared object */
Object a;

@Test() //succeeds
public void testFoo() {
    a = foo();
    Assert.assertNotNull(a);
}

@Test()
public void testBar() {
    Assert.assertNotNull(a); //fails
    b = bar(a); //null pointer exception
}

    


Ответы

Ответ 1



В общем - никак. Тесты должны быть независимы друг от дружки. Если они зависят от последовательности запуска - Вы неверно понимаете концепцию юнит тестов. Поэтому, их лучше писать так @Test() //succeeds // в этом тесте тестируется факт создания правильного объекта public void testFoo() { Object a; a = foo(); Assert.assertNotNull(a); // можно проверить другие поля объекта } @Test() public void testBar() { Object a; a = foo(); Assert.assertNotNull(a); //этот тест можно и пропустить, но лучше оставить // а вот тестить поля объекта уже не нужно // а теперь тестим код, который нуждается в этом объекте. b = bar(a); //null pointer exception } Но и эта схема "не очень правильная". В идеале пишут метод, который будет создавать объект и помещают его в @Before или что там предоставляет фреймворк.

Ответ 2



В JUnit есть аннотация @FixMethodOrder. Попробуйте таким образом: @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class SomeTest { @Test public void aTest() { /* your code */ } @Test public void bTest() { /* your code */ } }

Ответ 3



В целом, как уже заметили, это сделать невозможно (да и неправильно будет). Однако, если уж очень сильно хочется, то можно провернуть это через Suite`ы: import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ TestJUnitA.class, TestJUnitB.class }) public class TestSuite { @BeforeClass public static void beforeClass() throws Exception { } @AfterClass public static void afterClass() throws Exception { } } В приведенном примере TestJUnitA-класс выполнится заведомо перед TestJUnitB

Ответ 4



Согласно идеологии, юнит тесты должны быть изолированы друг от друга, каждый тест это независимая единица, те что зависимы друг от друга это уже не совсем юнит тесты. Другой вопрос, что нередко система настолько сложная и запутанная, что не выловить взаимовлияние тестов происходящее на каких то очень низких не подлежащих макетированию уровнях и вычистить взаимодействие на уровне JVM сложно, а играться с ClassLoaders тоже геморно, я для такого сделал маленький мавен плагин Jute, который просто запускает каждый тестовый метод JUnit в отдельном внешнем процессе в "чистой" jvm

пятница, 27 декабря 2019 г.

Junit и xml файлы

#java #xml #junit

Допустим, нужно протестировать функции калькулятора. В обычном случае на вход в тестах
подавались бы обычные числа. А как быть, если на вход нужно дать xml-файл?
    


Ответы

Ответ 1



Помещаете тестовый xml файлик как ресурс в тесты и передаете его в тестируемый метод. Файл можно передать как 'inputStream' или как текст.

Ответ 2



Не до конца понятен вопрос, но попробую ответить, так как в своих тестах тоже использую информацию из внешних файлов. Дальше идёт пример кода, который в каждом тестовом методе обеспечивает доступ к XML документу. package ru.test; import java.io.File; import java.io.IOException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.w3c.dom.Document; import org.xml.sax.SAXException; /** * Тестовое приложение, которые использует в тестах данные из XML документа. */ public class TestApp { private Document document; /** * Метод, который будет вызываться перед каждым тестом в файле. * Загружает и парсит необходимый XML документ. */ @Before public void setUp() throws ParserConfigurationException, SAXException, IOException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); // Здесь можно указывать любой файл в classpath и не только. document = builder.parse(new File("src_test/test.xml")); } /** * Метод, который вызывается после каждого теста. * Здесь можно ничего не делать. */ @After public void tearDown() { document = null; } /** * Сам тест. * Здесь получаем какую-то очень нужную информацию из XML документа. */ @Test public void testDocument() { // Запрашиваем элементы data из XML документа. document.getElementsByTagName("data"); } } В методе testDocument вы можете выбирать из уже загруженного XML документа необходимые для теста данные.

четверг, 19 декабря 2019 г.

OutOfMemoryError: GC overhead limit exceeded при параметрах -Xmx8192M -XX:-UseGCOverheadLimit

#java #junit #jvm #сборщик_мусора

jUnit тесты работают с большими объемами данных, в частности, данная ошибка возникает,
когда считываем данные из базы в объект.

ОЗУ - 16 Гб, 4 виртуальных процессора

когда возникла ошибка

java.lang.OutOfMemoryError: GC overhead limit exceeded
at sun.reflect.GeneratedConstructorAccessor47.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
at java.lang.Class.newInstance(Class.java:442)


то я добавил параметр -XX:-UseGCOverheadLimit
но получил следующую ошибку:

java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.lang.reflect.Field.copy(Field.java:150)
    at java.lang.reflect.ReflectAccess.copyField(ReflectAccess.java:144)
    at sun.reflect.ReflectionFactory.copyField(ReflectionFactory.java:309)
    at java.lang.Class.copyFields(Class.java:3115)
    at java.lang.Class.getFields(Class.java:1557)
    at main.java.DataFormat.DbTable.loadObjectFromResultSet(DbTable.java:101)
    at main.java.DataFormat.DbTable.getEntities(DbTable.java:57)


все параметры JVM -Xmx8192M -XX:-UseGCOverheadLimit

в момент появления ошибки были следующие параметры системы: 
ОЗУ стабильно на 46% (7400Мб), ЦПУ 77%-82% (82% в момент появления ошибки) 

подскажите, как исправить данную проблему? 

Спарвка


  
  java.lang.OutOfMemoryError: GC overhead limit exceeded
  
  
  Данная ошибка может возникнуть как при переполнении первой, так и
  второй областей. Связана она с тем, что памяти осталось мало и GC
  постоянно работает, пытаясь высвободить немного места. Данную ошибку
  можно отключить с помощью параметра -XX:-UseGCOverheadLimit, но,
  конечно же, её надо не отключать, а либо решать проблему утечки
  памяти, либо выделять больше объема, либо менять настройки GC.


Ответ
String объект слишком много весит. а у меня там очень много стрингов. Для примера,
посчитали файл, который весит 250 МБ, имеет 1600 символов в строке и 220 000 строк.
Заполняю в ArrayList lines, где каждая строка записана как объект из 250 полей,
которые принимают значение из 1600 символов. Такой объект весил чуть больше 6Гб. 

Решение: увеличил память до 14Гб. теперь проблема в том, что 6Гбайтный объект не
помещается в куче, надо думать что-то другое и пожертвовать временем выполнения теста. 
    


Ответы

Ответ 1



В данном случае ошибка java.lang.OutOfMemoryError: GC overhead limit exceeded говорит о том, что Вы пытаетесь обработать такой объем данных который не помещается в память. В приведенной Вами справке упомянуто, что ошибка выбрасывается, когда область памяти, занимаемая только, что созданными объектами переполняется. Плюс к этому в трассировке стека видно, что ваши классы ( at main.java.DataFormat.DbTable.loadObjectFromResultSet(DbTable.java:101) at main.java.DataFormat.DbTable.getEntities(DbTable.java:57) ) в момент получения ошибки как раз пытаются выгрузить данные из базы. Поэтому простой и неутешительный вывод из вышесказанного тестируйте на меньшем объеме данных или выделяйте больший объем под выгружаем данные(-Xmx12000M) если они туда поместятся, то ошибка исчезнет. Настраивать сборку мусора в данном случае бесполезно т.к. GC не сможет удалить загружаемые объекты потому как они еще используются.

Ответ 2



Начту с основ для лучшего понимания ошибки. JVM имеет две области памяти: Heap Memory и Non-Heap Memory. Heap Memory - хранит объекты; Non-Heap Memory - хранит параметры методов, примитивные типы и т.д. В Вашем случае происходит переполнение Heap Memory, т.к. очень много создается объектов, которые не вмещаются в Heap Memory. Решить проблему можно увеличив Heap Memory (ключ -Xmx), что не всегда помогает, т.к. объем данных может быть больше чем доступно ОЗУ, поэтому лучше реализовать обработку данных из БД порционно, т.е. выгружать часть данных, чтобы объекты поместились в памяти, затем убивать их (присваивать null, чтобы GC разгрузить), замет следующею часть и т.д. P.S. Стоит понимать, что Heap не равно ОЗУ. Heap - это только зарезервированная память для JVM, больше данного резерва JVM использовать не может даже, если ОЗУ в избытке.

Как правильно тестировать исключение?

#java #junit #тестирование

Есть некая функция DoIt, на которую пишется тест (JUnit). Если передать аргумент
null, функция должна выкинуть исключение. Я проверяют так:
public final void TestDoIt() {
    try {
        // Вызываю функцию
        DoIt(null);
        fail("doit(null) должна выкинуть исключение");
    } catch (NullPointerException e) {
    }
}

Это правильно или можно как-то лучше?
    


Ответы

Ответ 1



Это правильно для JUnit до версии 4. В новых версиях можно (и нужно) использовать аннотации: @Test(expected = NullPointerException.class) public final void doIt() { DoIt(null); }

суббота, 14 декабря 2019 г.

Тестирование Android JUnit

#android #junit

Решил разабраться, что же такое тестирование. Пару статей на хабре, в Intellij Idea
 через Maven  подключил JUnit, посмотрел примеры, написал пару тестовых методов с аннотацией
@Test -  все работает. 

Потом решил на примере тестового приложения Android сделать то же самое: 


в build.gradle (Module:app) добавил зависимость

dependencies {

    testCompile 'junit:junit:4.12'

}

скачал и подключил библеотеку junit-4.10.jar
написал такой тест 

import android.test.ActivityTestCase;

import junit.framework.Assert;


public class ChronometrTest extends ActivityTestCase {


public void testOne(){
    System.out.println(":::::::::::::::::::::testOne() start.. ");
    Assert.assertTrue(true);
}

}



жму правой кнопкой Run/ JUnit test, вот что я получаю в ответ:

Exception in thread "main" java.lang.NoClassDefFoundError: junit/textui/ResultPrinter
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:260)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:122)
Caused by: java.lang.ClassNotFoundException: junit.textui.ResultPrinter
at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 3 more


*Android studio v1.2.2

Вопрос: чего я не сделал, или сделал, но не так?
    


Ответы

Ответ 1



Актуально для Android studio. Для написания Unit тестов использую Robolectric и JUnit4. Robolectric нужен для получения доступа к специфическим для Andoird компонентам, например Context. Для использования JUnit4 и Robolectric надо дописать в build.gradle: testCompile 'junit:junit:4.12' testCompile "org.mockito:mockito-core:1.9.5" testCompile "org.robolectric:robolectric:3.0" После чего создать папку test рядом с папкой main, должна получится примерно следующая структура: app\src\main\ app\src\test\ В папке test лучше создать такую же структуру пакетов, как и в папке main. Хочу заметить что если в обозревателе проектов выбран тип Android, то папка test не будет видна. Необходимо переключить на тип Project. Так же чтобы все Robolectric и JUnit4 классы были доступны необходимо переключить Test Artifact в BuldVariant на Unit Test. После чего можно начать писать сами тесты. Тестовый класс должен иметь следущющую структуру: @RunWith(RobolectricTestRunner.class) @SmallTest public class SomeTestClass { @Test public void someTest(){ // код, тестирующий что либо } //остальные тесты } Для запуска необходимо создать JUnit конфигурацию и запускать ее, либо через командую строку. Все юнит тесты будут запускать локально, т.е нет необходимости подключать телефон или запускать эмулятор. UPD: если переключить Test Artifact на Unit Test, то папка test будет видна в типе проекта Android.

Ответ 2



Обычно это вызвано неправильным выбором конфигурации: JUnit вместо Android Test

вторник, 10 декабря 2019 г.

Отличия assertSame и assertEquals в JUnit

#java #junit

JUnit: в чем отличие assertSame(Object, Object) от assertEquals(Object, Object) именно
на параметрах типа Object?
    


Ответы

Ответ 1



assertEquals() использует метод equals(), если он определен. Таким образом, можно использовать свой принцип сравнения объектов. Если он не определен - работает как assertSame(). assertSame() просто сравнивает объекты при помощи оператора ==, то есть проверяет, являются ли параметры ссылками на один и тот же объект. Отсюда следует, что для параметров типа Object разницы нет.

Ответ 2



Если runtime-типы обоих параметров - java.lang.Object, то ничем.

суббота, 30 ноября 2019 г.

Как узнать процент покрытия кода тестами JUnit в IntelliJ IDEA?

#java #intellij_idea #юнит_тесты #junit

Как узнать процент покрытия кода тестами JUnit в IntelliJ IDEA?
    


Ответы

Ответ 1



Тут IntelliJ IDEA 2017.1 Help :: Running with Coverage все подробно описано. То есть запускаете тест с Покрытием В правом боку "Editor" откроется таблица в которой показано покрытие кода в процентах и внизу появится пункт "Coverage".

Ответ 2



Edit Configurations -> Add new configuration -> JUnit Test kind: All in package и в поле package выберите корневой пакет проекта (Если вам нужно прогнать все тесты) На закладке "Code Coverage" выберите корневой пакет проекта (опять же, если надо прогнать все тесты). Конфигурацию настроили. Её необходимо запускать кнопкой "Run with Coverage" (см. картинку ниже). В результате вы получите процент покрытия, а в подарок увидите детализацию, какие именно строки покрыты, а какие - нет.

воскресенье, 24 ноября 2019 г.

Как писать простейшие UnitTest'ы к простейшим функциям?

Хочу научиться писать тесты для своих проектов. 

Подскажите какие-нибудь хорошие ресурсы, чтобы научиться тестировать Android приложения.

Насколько важно их использование? 

Сейчас я пишу приложение без их использования и пока не могу оценить их пользу.

Те коды, которые встречаю в интернете, не помню, чтобы где-то в коде встречал тесты. 

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

Допустим есть вот такой метод:

private File getFile(File path) {
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    return new File(path.getPath() + File.separator + timeStamp + ".html");
}


Как можно к нему написать тест и что нужно для этого сделать?

ПРАВКА

Вот кстати есть ссылка с видео где показан пример теста 

https://www.youtube.com/watch?v=ZJE0MDKJOow
    


Ответы

Ответ 1



Для того чтобы создать юнит тест, вам прежде всего нужно определиться что вы собственн собираетесь тестировать. В идеале, ваш метод должен делать что-то одно и тогда ваш задача упрощается. Если возможно, то юнит тест должен тестировать метод как черный ящик, то есть, вы подаете что-то на вход и проверяете полученное значение. К сожалению это не всегда возможно. В вашем конкретном случае, первое что бросается в глаза, это то что метод помече как private, такой метод нельзя тестировать стандартными методами. Существует мног теорий насчет того нужно ли тестировать приватные методы или нет. Мое мнение - их можно не тестировать. Правильность работы приватных методов будет проверена неявно когда вы тестируете все открытые методы. Хотя если вы очень хотите то можно пометить метод как 'default' (убрать тип доступa), или можно использовать reflection. Но допустим ваш метод публичный, тогда анализируя код понимаем, что метод возвращае новый файл с именем построенным из пути и функции текущей даты. То есть, он делает два действия: * Создает имя файла * Создает собственно файл Тестировать его в текущем виде можно но не интересно. Я бы посоветовал немного поправить ваш код чтобы он был более пригодным для тестирования: public File getFileFullName(File path, Date date) { String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); String fullPath = path.getPath() + File.separator + timeStamp + ".html"; return fullPath; } // где-то в вашем коде String path = ... Date now = new Date(); String fileFullName = getFileFullName(path, now); File file = new File(fileFullName); // тест для метода getFileFullName в классе ClassUnderTest @Test public void testGetFileFullName() { String path = "/abc"; Date date = new Date(1465953124); //2016-06-15 13:12:06 ClassUnderTest instance = new ClassUnderTest() String name = instance.getFileFullName(path, date); assertEquals("/abc/20160615_131206.html"); } Но вы можете спросить, а как тестировать код где мы создаем дату или используем файл Касательно даты, я бы рекомендовал вообще не использовать new Date() в коде. Вмест этого использовать что типа провайдера или сервиса для получения текущей даты/времени. Тогда вы можете подменять реализацию этого класса для тестирования. Простейшая реализация может быть синглтон: public class DateTimeUtils { private static DateTime fixedTime; public static DateTime getCurrentDateTime() { if (fixedTime == null) { return new DateTime(DateTimeZone.UTC); } return fixedTime; } public static void useFixedCurrentTime(DateTime timeToReturn) { fixedTime = timeToReturn; } } Что касается объекта File то здесь вам поможет подмена классов с помощью мокирования. Посмотрите Mockito или PowerMock. Еще маленькое дополнение. Если бы вы использовали test-driven development то такой проблемы бы не было.

Ответ 2



Гораздо проще писать тесты для ещё не написанного кода(разработка через тестирование) нежели пытатся приделать тесты к готовому. Просто посмотрите на задачи, опишите проверк их выполнения в виде тестов, и только потом напишите код, который эти тесты успешно выполнит. Не полностью для всего проекта, а максимально небольшими шагами, состоящими из постановки задачи, написания теста и рабочего кода к нему. Придумывание тестов к готовому коду - очень ресурсозатраная задача. Как в плане планирования(ва нужно вспомнить или выяснить зачем рассматриваемый код вообще был написан), так и плане реализации(обычно нужен рефакторинг, много-много рефакторинга). Созданные "постфактум" тесты порой оказываются бесталковыми, написанными просто ради "близкого к 100% покрытия", а на деле не проверяющими то, что действительно стоило бы проверять... Чаще всего оказывается дешевле и быстрее просто постепенно заменять старые модули новыми, написанными с нуля через тестирование, нежели писать тесты к уже имеющимся.

понедельник, 15 июля 2019 г.

Companion object в Kotlin

Только перехожу с Java на Kotlin. Возник вопрос, ответ на который, к сожалению, я не смог найти. Существует некий абстрактный класс с несколькими абстрактными переменными, на который потом будут ложиться десятки тестов. Тестирую с помощью JUnit. Методы аннотированные @BeforeClass и @AfterClass обязаны быть статичными, и я вижу только один способ разрешения пробелемы: запихнуть методы внутрь companion object, где можно использовать @JvmStatic, но при этом, в методе @BeforeClass вызывается абстрактная переменная, задаваемая каждой реализацией отдельно. Соответсвенно, каким образом можно обратиться к переменной из внешнего класса? Или может быть есть другой способ разрешения такой задачи? Код:
abstract class TemplateConfig {
abstract val template : String?
companion object { lateinit var h: Handle
@BeforeClass @JvmStatic fun setUp() { h = dbi.value.open() //Здесь используется абстрактная переменная // //if (template != null) { // h.createStatement(template).execute() //} }
@AfterClass @JvmStatic fun tearDown() { h.close() }
//{...Объявление и инициализация других переменных...} } }


Ответ

Если я правильно понял, вы пытаетесь в статическом методе обратиться к нестатической переменной/методу которая задается в классе потомке. Так не выйдет. Единственное решение, которое я вижу, это определить поле статическим и при инициализации класса-потомка задать ему значение.
object Demo { protected var value:String @BeforeClass fun setUp() { println(value) } @AfterClass fun destroy() { } } internal class SubClass:Demo() { companion object { init { Demo.value = "this is value" } } }