• Как быстро и эффективно мокать реляционные БД для тестов?

    BorLaze
    @BorLaze
    Java developer
    Лично я предпочитаю инструменты типа EmbeddedPostgres.

    Дольше моков, безусловно, но зато это практически полный аналог работы в проде - БД поднялась, проинициализовалась данными, мы выполнили нужные нам действия (тот же insert или select любой сложности), умерла.

    Ибо моки - это, конечно, хорошо, но как быть, если структуру базы мы уже поменяли, а тесты нет?
    Код работает со старыми моками, все хорошо - а в реальной работе бац, и "все умерли"...
    Ответ написан
    Комментировать
  • Как быстро и эффективно мокать реляционные БД для тестов?

    xez
    @xez Куратор тега Java
    Senior Junior Roo
    mockito -- довольно удобно делать заглушки. Но это, скорее, для юнит тестов.
    или
    testcontainers -- для полноценных интеграционных тестов. Нужно чтобы на машине стоял докер. Это не эмуляция БД - это поднимается настоящая БД в докере. После тестов оно само все умирает.
    Ответ написан
    Комментировать
  • Что дает паттерн билдер по сравнению с обычными сеттерами?

    @MarkusD
    все время мелю чепуху :)
    Для начала стоит обратиться к какому-нибудь источнику информации об этом шаблоне. Например, к этому.
    Представьте сложный объект, требующий кропотливой пошаговой инициализации множества полей и вложенных объектов. Код инициализации таких объектов обычно спрятан внутри монструозного конструктора с десятком параметров. Либо ещё хуже — распылён по всему клиентскому коду.


    Иными словами, под монструозным конструктором понимается намеренное создание монолита. Монолит создается, как правило, один раз и потом тухнет до полного разложения его кода, когда пользоваться им уже невозможно в следствии объема его ошибок, но и отрефакторить тоже невозможно в следствии его монолитной организации.

    А под ещё хуже понимается именно вот этот метод с интерфейсом полным сеттеров, которые призваны инициализировать объект до его правильного состояния. Только вот в чем проблема: в каком состоянии будет находиться объект до вызова всех нужных сеттеров и завершения его инициализации? В неправильном состоянии, но объект уже будет и воспользоваться им в таком состоянии кто-то обязательно захочет. А ведь еще кто-то должен знать эту самую последовательность(последовательности?) вызова сеттеров чтобы завершить инициализацию такого объекта. Не многовато ли избыточной ответственности на клиентский код возлагается?

    Давай представим что нам надо выполнить рейс на МИ-8. Но чтобы автоматизированная система современного аэродрома дала нам разрешение на взлет, нам надо передать ей специальный хеш.
    Как это могло бы быть...
    МИ-8 - старая штука, на ее борту механический бортовой компьютер - что-то по аналогии с машиной Ч. Беббиджа, только поменьше. Современные машины формируют этот хеш сами из своей телеметрии процесса запуска и маршрута полета. У нас же с собой только мобильник с ПО для формирования этого хеша вручную и обширная приборная панель МИ-8.
    И вот, мы приступаем к запуску МИ-8: запускаем каждый механизм и вписываем показания приборов в ПО на мобильнике. Когда вертолет запущен и план полета установлен, мы жмем в ПО на мобильнике кнопку генерации хеша и получаем строчку этого хеша.
    В связи с тем, что старой техники со своими условностями море, а формат хеша единый для всех, ПО ручной генерации хеша для каждой модели техники сделали отдельно от ПО передачи хеша в диспетчерскую аэродрома. Да и вообще, ПО генерации хеша для старой техники пишется в КБ производителя этой техники. Поэтому нам выдали строковое представление хеша и теперь его надо скопировать из одного ПО и вставить в нужное поле для отправки в другом ПО...

    Так, стоп. Кажется хеш - это интерфейс, оператор ПО внезапно оказался посредником, а ПО для ручной генерации хеша - это билдер что ли? Все именно так.
    Билдер хорошо знает устройство того типа, который он строит. Но ни о самом этом типе, ни о его устройстве посреднику можно не знать. Посредник может знать только интерфейс (это важно, т.е. даже не сам тип билдера, а только его интерфейс) билдера и интерфейс создаваемого объекта. Посредник имеет право обойтись малыми знаниями, которых достаточно для того чтобы передать артефакт работы билдера потребителю.
    Билдер может строить объект совсем другого типа данных, отдает он всегда некоторый обобщенный интерфейс.
    Вдобавок, создаваемый нашим билдером из примера хеш является DTO - т.е. Data Transfer Object, среди прочих свойств которого можно обозначить иммутабильность.


    Иммутабильный объект можно получить через идиому RAII или фабричного метода. Но что если для создания иммутабильного объекта требуется очень много параметров? Например - 16 параметров. Или 23, как в одном случае. Или - 42 параметра.
    Что если часть этих 42 параметров можно вычислить из остальных, но вот иногда их нужно указывать явно?
    Что если всего параметров 42, но для конструирования требуется использовать лишь произвольное подмножество этих параметров? Припоминаются SQL-запросы, правда ведь?
    RAII в этом случае захлебывается и становится непонятным, а фабричных методов требуется столько, что ими становится тяжело управлять. Идиома фабричного метода в этом случае начинает проявлять свои негативные качества и тормозить разработку.
    А билдер со всеми такими случаями легко справляется. Напомню, что Immutable в принципе невозможно снабдить сеттерами, т.к. это нарушит иммутабильность. А если выйти из ситуации через преобразование одного Immutable в другой, то достичь таким способом получится лишь комбинаторного взрыва иммутабильных типов, похоронив тем самым дальнейшую разработку.

    Но давай представим другой пример. Допустим, система диспетчеризации аэродрома принимает сигнал о готовности взлета со стороны борта. Разрешать взлет или нет? Кажется, сперва надо то-то проверить перед резолюцией.
    Как это могло бы быть...
    Нужно осмотреть журналы движения судов, сверить полетные расписания, сделать сверку документов пилота на допуск к полетам сегодня и еще что-нибудь малозначительное, вроде проверки расписания уборки взлетного полотна, чтобы какой-нибудь уборщик не взлетел вместе с бортом на воздух.
    Все это - запросы к куче разных сервисов. Какой-то сервис может Быть слабонагруженным, а у какого-то могут быть перебои в работе. Система же распределенная и отказоустойчивая, в ней есть дублирующие узлы, на которые стоит отправить повтор запроса в случае отказа по запросу с основного узла.
    Все данные нужно собрать в экземпляре сложной аналитической системы, которая состоит из множества довольно сложных стратегий работы со своими данными. Это все значит, что объект, с которым должна работать система диспетчеризации, будет иметь крайне сложный инвариант своего типа. Такой инвариант невозможно собрать на сеттерах, объект просто не склеится. К тому же, зачем системе диспетчеризации знать все тонкости типа такой сложной аналитической системы, когда ей нужен просто ответ - можно вот этому борту взлетать или нет?
    Тут стоит отметить и то, что решение такой задачи в процедурном стиле приведет к все тому же комбинаторному взрыву и перегрузке по логике, потому что сервисный код запросов будет тесно переплетен с кодом логики принятия решений.
    Но система диспетчеризации может просто взять билдер и сформировать на его базе граф асинхронных задач запросов к внешним системам. Именно в топологии этого графа и отражаются зависимости запросов к резервным системам, а результаты запросов складываются в билдер в произвольном, полностью асинхронном порядке.
    Когда билдер возвращается к системе диспетчеризации из асинхронной среды, система диспетчеризации просто создает из него объект аналитической системы, с которой дальше и ведет все свои беседы по душам.


    Билдер призван решать вопросы перегрузки по знаниям типов, перегрузки по сохранению отказоустойчивости и перегрузки по сложности создания экземпляров. И использовать его стоит именно для решения таких задач.
    Еще билдер позволяет развести логику сеттеров отдельно от логики результирующего типа, это бывает очень полезно.
    И, да, важно отметить еще и то, что если инвариант типа допускает наличие сеттеров, если в том, что пользовательская система знает этот тип в лицо, нет проблемы, если сами сеттеры не перегружают логику типа, то билдер уже явно не нужен.
    Ответ написан
    1 комментарий
  • Куда можно задеплоить для тестов простое веб-приложение на Java?

    azerphoenix
    @azerphoenix Куратор тега Java
    Java Software Engineer
    1) Heroku
    2) Hetzner (Cloud VPS) 3 евро в месяц
    Ответ написан
    Комментировать
  • Куда лучше класть стабы и моки - вместе с исходниками или с юнит тестами?

    Chronicler
    @Chronicler
    Программист
    Есть хорошее правило, все что относится к тестам, ресурсы, стабы, моки, код -- должно храниться в тестовом пакете, чтобы не попадало в продакшн-сборку
    Ответ написан
    Комментировать
  • Не рано ли изучать Rust?

    sergey-gornostaev
    @sergey-gornostaev
    Седой и строгий
    Первым лучше учить тот язык, на который реально много вакансий. Это не только не Rust, но даже и не Go.
    Ответ написан
    7 комментариев
  • Как проверить, что данный элемент входит в enum?

    xez
    @xez Куратор тега Java
    Senior Junior Roo
    Java 8+
    import java.util.Arrays;
    import java.util.Scanner;
    
    public class Main {
        enum TestEnum {
            One(1), Two(2), Three(3);
    
            private int code;
    
            TestEnum(int code) {
                this.code = code;
            }
        }
    
        public static void main(String[] args) {
            int n = new Scanner(System.in).nextInt();
            boolean isPresent = Arrays.stream(TestEnum.values()).anyMatch(element -> element.code == n);
            System.out.println("n in TestEnum? " + isPresent);
        }
    }
    Ответ написан
    1 комментарий
  • Почему неправильно работает этот код?

    zagayevskiy
    @zagayevskiy Куратор тега Java
    Android developer at Yandex
    Это не так работает. Если бы Integer был мутабельный, и ты изменял его внутренности, то изменения были бы видны.
    Возвращай значение из метода.
    Ответ написан
    4 комментария
  • Почему неправильно работает этот код?

    @Kot1que
    пишу на жаве
    1. Integer - неизменяемый.
    2. https://stackoverflow.com/a/40523
    import java.util.Scanner;
    
    public class Main {
        private static Integer invertDigits(Integer number) {
            Integer temp = 0;
            while (number != 0) {
                temp = temp * 10 + number % 10;
                number /= 10;
            }
            return temp;
        }
    
        public static void main(String[] args) throws Exception {
            Scanner scanner = new Scanner(System.in);
            Integer number;
            System.out.print("Enter number: ");
            number = scanner.nextInt();
            System.out.println("Reverse digit order: " + invertDigits(number));
        }
    }
    Ответ написан
    1 комментарий
  • Можно ли установить часовой пояс в Linux по разнице во времени?

    @galaxy
    timedatectl set-timezone Etc/GMT+3
    Ответ написан
    Комментировать
  • Как преобразовать время в Timestamp без часового пояса (UTC)?

    @Kot1que
    пишу на жаве
    Timestamp ничего не прибавляет, Timestamp.toString() возвращает строку с учётом текущей зоны виртуальной машины.

    Timestamp stamp = new Timestamp(1593864000000L); // 07.04.2020 12:00 UTC
    System.out.println(stamp.toString()); // 2020-07-04 15:00:00 (Moscow UTC+3)
    System.out.println(stamp.getTime() == 1593864000000L); // true


    Oracle docs
    Ответ написан
    3 комментария
  • Как сделать, чтобы программа делала запись в БД в определенные часы?

    @koperagen
    stackoverflow (без спринга)
    Ответ написан
    Комментировать
  • Какая структуру выбрать для массива, где неважен порядок, а определяется только элементами?

    tsarevfs
    @tsarevfs
    C++ developer
    Есть несколько вариантов для хранения неориентированных графов.
    1. Хранить любую из дорог, а при доступе к ним проверять наличие какой-то из 2
    2. Хранить всегда только дорогу из города с меньшим номером в город с большим.
    3. Хранить 2 дороги всегда.

    В зависимости от задачи бывает удобнее использовать один из этих способов.
    Ответ написан
    Комментировать
  • Какая структуру выбрать для массива, где неважен порядок, а определяется только элементами?

    leahch
    @leahch
    Я мастер на все руки, я козлик Элек Мэк :-)
    Да хоть, если даже дорога проходит через 10 городов - используйте TreeSet().

    SortedSet road1 = TreeSet<City>();
    road1.add("Mos");
    road1.add("Spb");
    
    SortedSet road2 = TreeSet<City>();
    road2.add("Spb");
    road2.add("Mos");
    
    assert road1.equals(road2);
    Ответ написан
    Комментировать
  • Какая структуру выбрать для массива, где неважен порядок, а определяется только элементами?

    @antonwx
    Вам надо не создавать новый объект, а делать метод-селектор, который будет проверять существование дороги и если она существует - возвращать существующую, если нет - то создавать новую. В данном случае можно реализовать проверку так:
    Set<Road> roads = new HashSet<Road>();
    Road getRoad(c1, c2){
    for(Road r:roads) if((r.city1.equals(c1) && r.city2.equals(c2)) || (r.city2.equals(c1) && r.city1.equals(c2))) return r;
    Road r = new Road(c1, c2);
    roads.add(r);
    return r;
    }

    Если городов будет больше, имеет смысл использовать сеты вместо перечислений городов в отдельных переменных.
    P.s. писал с мобильника, могут быть ошибки
    Ответ написан
    Комментировать
  • Существует ли "карта программиста"? Что и за чем учить?

    Epsiloncool
    @Epsiloncool
    Программер, веб-девелопер, гейм-девелопер
    Я программист с 15-летним стажем активной работы. Программирование - это инструмент для разработки ПО. Такой же как умение ходить для свободного перемещения из точки А в точку Б. Когда ребёнок рождается, нет никакой карты, в которой бы было указано - в какой последовательности он должен изучать ходьбу, чтобы стать в итоге полноценным человеком. Так и в разработке ПО - нет никакой последовательности. Вам нужно изучать всё сразу, понемногу. Причём не теоретически, а практически. Ребёнок не читает книг по развитию умения ходить, не слушает лекции от родителей. Он сразу пробует. Падает, и снова пробует. Пока не научится. С разработкой ПО в точности так же.

    Нет никакого смысла читать книги по изучению конкретного языка. Ставьте задачу - "переместиться из точки А в точку Б" (сделать какое-то конкретное приложение) и гуглите по каждому непонятному моменту, пока программа не будет написана. Научитесь правильно строить поисковые запросы.

    После того как вы с большим трудом запустите свой первый продукт. вы уже будете знать и уметь в десятки раз больше, чем студент, окончивший пятилетний курс по специальности "программирование" и прочитавший пару толстых теоретических книг.
    Ответ написан
    6 комментариев
  • Как так расположить дивы?

    potapchino
    @potapchino
    Ответ написан
    Комментировать
  • Вопрос по синтаксису Qt?

    @poslannikD
    Java/C/C++ Programmer
    Это вопрос по синтаксису с++ )
    QLabel *label = new QLabel("Привет");
    label-> show();

    давайте разберем построчно
    1) QLabel("Привет"); - создали экзепмляр класса QLabel
    2) new QLabel("Привет") - оператор new выделяет память в куче, размещает там объект
    QLabel("Привет")
    и возвращает указатель на адрес в куче. По этому адресу расположен наш объект
    3) = new QLabel("Привет"); - оператор = выполняет присваивание(или инициализацию в зависимости от контекста). Присваивание(инициализация) чего ? Присваивание(инициализация) правого операнда. Присваивание(инициализация) чему? Присваивание(инициализация) левому операнду.
    4) QLabel *label - объявили указатель, который может указывать на объекты класса QLabel.
    5) QLabel *label = new QLabel("Привет"); -читаем с Права на лево создали объект, разместили его в куче, адрес объекта передали указателю типа QLabel который расположен слева от знака =
    Я так понимаю что [*label] это указатель на адрес объектов которых в дальнейшем будет много наверное

    Это указатель на адресс ОБЪЕКТА, этот указатель в один момент времени указывает только на ОДИН объект. так как отсутствует ключевое слово const то, да он может указывать на разные объекты. Но в конкретный момент времени он указывает только на один объект или вникуда

    label-> show();
    - обращаемся к объекту через УКАЗАТЕЛЬ на этот объект

    SomeClass a; //тут создается объект на основе класса
    SomeClass *p = &a; //это указатель на адрес объекта чтобы через него // вызывать методы класса

    1) SomeClass a; - объект создается на СТЕКЕ, не рекомендуется создавать тяжеловесные объекты на стеке, а легковесные можно.
    2)SomeClass *p = &a; - указатель указывает на объект на стеке все норм :)

    Различие между
    SomeClass a;
    и
    new QLabel("Привет");
    в том что объект а существует на стеке, а QLabel("Привет"); в куче
    Ответ написан
    Комментировать
  • Как найти координаты одной системы координат в другой по заданной прямой?

    hint000
    @hint000
    у админа три руки
    Для простоты рассмотрим частный случай: прямая проходит через начало координат и в 1-й, и во 2-й системе, повёрнуты системы одинаково. Получается, что уравнения прямой в двух системах полностью совпадают, а сдвиг между системами может быть какой угодно. Т.е. невозможно однозначно определить, решений будет бесконечно много.
    Ответ написан
    Комментировать