На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела:
1. Название темы - краткое описание кто/что против кого/чего
2. В первом сообщении - список параметров, по которым идет сравнение.
3. Старайтесь аргументировать свои высказывания. Фразы типа "Венда/Слюникс - ацтой" считаются флудом.
4. Давайте жить дружно и не доводить обсуждение до маразма и личных оскорблений.
Модераторы: Модераторы, Комодераторы
Страницы: (16) « Первая ... 13 14 [15] 16  все  ( Перейти к последнему сообщению )  
> Rust vs C++ , очередная шляпа
    D_KEY, мне кажется мы о разном говорим.
    У меня одна картина в голове стоит, у тебя вторая. Поэтому не понимаем друг друга. Ты мне пишешь про тестирование каких то несвязанных вещей, я напротив больше сталкивался с взаимосвязанными системами.
    Когда например у тебя один класс, инкапсулирует в себе кучу логики, которая инкапсулирует в себе другую кучу логики, которая инкапсулирует в себе еще какую то кучу логики.
    Я не знаю, может быть ты рассказываешь с философской точки зрения. Но хотел бы я посмотреть как ты все это тестировал бы.

    Например была такая система на позапрошлой работе, сервер Unix, клиент Windows. Есть продукт со своей логикой, к этой логике вешается требование, что клиент не зависит от версии сервера в принципе. Ну и архитектор налабал там систему классов по примеру Java Beans, т.е. чтоб написать одну сущность, тебе нужно сразу писать 4-5 классов. 1 класс - интерфейс, 2 класс - имплементация, 3 класс - фабрика, 4 класс - хендл, который в себе все это скрывает через 5 класс прокси объект. Все это завязано на каком то жестком контексте, который я разгребал пол года, чтобы понять из чего он состоит. Просто тупо целая куча самых разный мелких типов, персистентные объекты, какие то там еще объекты, это все еще обернуто впридачу высокоуровневыми шаблонами, десятиэтажное наследование. Ты пишешь какой нибудь класс WorkstationRedirection, который оперирует не вот этими классами A и B, а целыми объектами, потому что по другому ты не протестируешь, на все это есть ЮТ. Вот я бы посмотрел сколько бы ты писал свои моки для одного ЮТ, когда тебе нужно протестировать логику перемещения спесимена из одной рабочей станции на другую, где чтобы съимитировать объекты на моках, пришлось бы городить хренову тучу кода и тратить примерно столько же времени, сколько на разработку самого функционала. И моки там были, только нужны они были для замены сервера/клиента/БД/файлвой системы/ит.д. не более.

    А ты мне рассказываешь про какую то философию программирования.
      Цитата Wound @
      Ты мне пишешь про тестирование каких то несвязанных вещей, я напротив больше сталкивался с взаимосвязанными системами.
      Нет, он тебе рассказывает о технологии тестирования связанных систем по отдельности.
      Элементарнейший пример – функция вызывает функцию с параметрами: ты можешь написать отдельный тест на вызываемую функцию, которую прочекаешь по полной программе, по всем диапазонам классов эквивалентности всех её входов, не исключая параметры, и убедишься, что она ведёт себя полностью в рамках её модели поведения, описываемой документацией; затем ты пишешь другой тест, на этот раз на вызывающую функцию, соблюдая те же принципы и опираясь на её модель поведения.
      При создании второго теста ты вполне имеешь право застабить вызываемую, и пусть она ведёт себя не по её задокументированной модели, это уже неважно. Важно, что её поведение тесту известно, и его не надо тестировать, ведь по сути она его составная часть, и это поведение значительно проще, и им можно легко управлять из теста. При этом вместо того, чтобы учитывать модель поведения вызываемой функции, этому тесту достаточно лишь прочекать, что вызывающая (тестируемая на данном этапе) функция правильно выставляет входы для мока и правильно потребляет результаты его работы.
      Т.к. предыдущий тест чекает модель замоканой функции на любой общий случай, способы взаимодействия вызывающей с исходной вызываемой будут лишь какими-то частными их случаями, покрываемыми предыдущим тестом, и на основании результатов этих двух тестов можно сделать экспертный вывод о корректности связки обоих функций. Если экспертного вывода по какой-то причине недостаточно, такое редко, но бывает, то взаимодействие этих двух функций может быть покрыто третьим тестом, но его сферой ответственности будет лишь потоки данных и управления, но не модели управления целиком.
        Цитата Qraizer @
        Нет, он тебе рассказывает о технологии тестирования связанных систем по отдельности. Элементарнейший пример – функция вызывает функцию с параметрами: ты можешь написать отдельный тест на вызываемую функцию, которую прочекаешь по полной программе, по всем диапазонам классов эквивалентности всех её входов, не исключая параметры, и убедишься, что она ведёт себя полностью в рамках её модели поведения, описываемой документацией; затем ты пишешь другой тест, на этот раз на вызывающую функцию, соблюдая те же принципы и опираясь на её модель поведения. При создании второго теста ты вполне имеешь право застабить вызываемую, и пусть она ведёт себя не по её задокументированной модели, это уже неважно. Важно, что её поведение тесту известно, и его не надо тестировать, ведь по сути она его составная часть, и это поведение значительно проще, и им можно легко управлять из теста. При этом достаточно лишь прочекать, что вызывающая (тестируемая на данном этапе) функция правильно выставляет входы для мока и правильно потребляет результаты её работы.

        Ну когда речь идет об элементарнейших вещах, я это могу понять. Но когда речь идет о какой то хитрой логике(не все же методы возвращают сумму двух чисел или например константную строку), то тут тебе придется мочить все сущности, которые используются в тестируемом алгоритме, а если вдруг какая то сущность внутри получается из другой сущности, или просто живет там, конструируясь на каких то данных, то придется в придачу еще и переписывать всю систему, то тут уж, извиняйте, но я понять не могу. Если речь идет о какой то философии совершенного кода - ну тогда так и говорите. Я как бы исхожу из реальности, а не из каких то там аксиом и теоретических совершенностях.
        Сообщение отредактировано: Wound -
          Цитата Qraizer @
          Ещё одно хорошее "зачем": разве не достаточно тестом показать факт наследования тестируемого класса от интересующего нас базового? Разве не следует из гарантий языка программирования заранее известный (однозначно предсказываемый) итог запуска тестов на наследуемых методах?

          Нет, не следует, метод может быть переопределён.
            Вклинюсь тут. А никто не поделится ссылкой на какой-нибудь более-менее крупный проект (желательно на плюсах), где тестирование делается как раз вот так, как тут описано - с активным mock-анием всего кроме тестируемой сущности? А то я тоже слабо представляю, что делать, если сущности оказываются сильно связанными.
              Цитата Wound @
              Но когда речь идет о какой то хитрой логике(не все же методы возвращают сумму двух чисел или например константную строку), то тут тебе придется мочить все сущности, которые используются в тестируемом алгоритме, а если вдруг какая то сущность внутри получается из другой сущности, или просто живет там, конструируясь на каких то данных, то придется в придачу еще и переписывать всю систему, то тут уж, извиняйте, но я понять не могу.

              А можешь привести такой пример? Было бы интересно рассмотреть.

              Добавлено
              Цитата OpenGL @
              А то я тоже слабо представляю, что делать, если сущности оказываются сильно связанными.

              А ты сможешь привести пример?
                Цитата D_KEY @
                А ты сможешь привести пример?

                Пример чего? Связанных сущностей? Да любой контейнер со своими итераторами, в общем-то - если это не тупо вектор, а какая-либо более-менее нетривиальная структура данных, то с большой вероятностью они друг с другом будут связанными. Недавно вообще вот такое писал:
                ExpandedWrap disabled
                  for(auto x : my_container.indexer().set_param_1().set_param_2()){}

                Т.е. чтобы можно было настраивать итерирование по этому контейнеру. Сущность, возвращаемая методом indexer() оказывается тоже сильно связанной с контейнером, как и итераторы. У меня вообще получилось, что она является friend-ом для него. Так что по факту и контейнер, и эта сущность, и все типы итераторов это один-единственный класс, и поэтому тестировать их отдельно друг от друга это, фактически, совмещение неприятного с бесполезным.
                  Цитата OpenGL @
                  Да любой контейнер со своими итераторами

                  Ну я не уверен, что стал бы тестить отдельно. Это же не внешняя зависимость для контейнера.

                  Добавлено
                  Цитата OpenGL @
                  Так что по факту и контейнер, и эта сущность, и все типы итераторов это один-единственный класс, и поэтому тестировать их отдельно друг от друга это, фактически, совмещение неприятного с бесполезным.

                  Тогда и не надо так делать :-?

                  Речь шла про внешние зависимости для класса, причем такие, которые уже через интерфейсы работают. Киля предлагает в тестах использовать конкретные классы системы, реализующие эти интерфейсы. Я предлагаю замокать.

                  Добавлено
                  OpenGL, вот если бы у тебя был какой-то Iterator, который мог бы работать с любыем IContainer, то я бы в тестах Iterator'а замокал IContainer, а не тащил бы какую-то из его реализаций в тест.
                    А, не, если там уже интерфейс, то лучше замокать, естественно. Причём если тесты нужны побыстрее и ещё вчера, можно поначалу за ним вообще реальный объект спрятать :D
                      Цитата korvin @
                      Нет, не следует, метод может быть переопределён.
                      Если ты имеешь в виду "перекрыт", подразумевая полиморфное поведение, то унаследованный метод вообще не при делах. Он либо просто исключается из логики поведения производного, либо на его наличие производный всё-таки рассчитывает, и тогда задача сводится к последующей. Которая вот она: если же ты подразумеваешь, что это новый метод, то у производного класса будет в итоге будет два метода... ну и ты уже понял: новый метод тестится новый тестом, унаследованный, как я и отметил ранее, покрыт гарантиями языка и своим, уже написанным, тестом.
                      В любом случае, выбор между вариантами определяется бизнес-логикой и архитектурой, а не правилами "как правильно тестировать". Следовательно тестировать унаследованные методы в тестах на производные классы не требуется, если так или иначе показан факт наличия этой родственной связи между классами. Если что-то где-то когда-то пойдёт не так на рефакторинге, какой-нибудь тест да упадёт, так что ложных пассов не будет.

                      Добавлено
                      Цитата Wound @
                      Но когда речь идет о какой то хитрой логике(не все же методы возвращают сумму двух чисел или например константную строку), то тут тебе придется мочить все сущности, которые используются в тестируемом алгоритме, а если вдруг какая то сущность внутри получается из другой сущности, или просто живет там, конструируясь на каких то данных, то придется в придачу еще и переписывать всю систему, то тут уж, извиняйте, но я понять не могу.
                      Не придётся. Берётся минимально возможная единица декомпозиции, в Джаве это класс, например, на Сях/Плюсах единица трансляции, и вокруг него строится каркас их моков. Можно даже не работать с сырцами при этом, достаточно готовых .jar или там .obj, которые вполне подходят для линковки с тестом. Фактически тест и является приложением, а тестируемая сущность его небольшая составная часть, лишь бы она осталась неизменной по сравнению с продашновой. Один раз сделал такой каркас, что может занять не так уж мало времени, но технически реализуется несложно, и готов писать тесты на всё из этой единицы декомпозиции уже без ресурсозатрат. Для другой единицы декомпозиции основной каркас уже есть, нужно лишь исключить её моки и добавить ещё комплект, стабирующий вон ту, с предыдущего этапа. С этого момента у тебя есть всё для всего.
                      Заметь, на этапе компонентного тестирования от тестового окружения не требуется каких-либо релеваций с описанным в документации поведением системы. Нужны экземпляры тестируемых классов – ну и создавай их как тебе удобно. Нужны внешние связи, пиши простейшие стабы, откликающиеся на них удобным тебе образом. Из документации на ПО требуется только описательная часть интерфейсов и архитектуры. Из поведения не требуется ничего. Естественно кроме того самого маленького фрагмента, который собственно тестируется. То, что ты описываешь в своих примерах, это уже другой уровень тестирования. Возможно интеграционный, на котором (если, конечно, тестами низкого уровня действительно показана корректность реализации каждого элемента декомпозиции) заостряться на поведении каждой сущности просто уже нет необходимости, достаточно ограничиться лишь их связями по данным и управлению.
                      Сообщение отредактировано: Qraizer -
                        Цитата Qraizer @
                        Можно даже не работать с сырцами при этом, достаточно готовых .jar или там .obj, которые вполне подходят для линковки с тестом.

                        Я правильно понял, что ты предлагаешь что-то такое:
                        ExpandedWrap disabled
                          // foo.h
                           
                          #include "bar.h"
                          // Это тестируемый класс
                          class Foo
                          {
                              Bar bar;
                          };
                           
                          // bar.h
                          class Bar
                          {
                              // Методы должны быть реализованы в cpp, т.е. шаблонный класс не катит
                              void method();
                          };

                        И просто юзать в приложении "официальный" bar.cpp, а в тестах bar_mocked.cpp?
                          Цитата D_KEY @
                          Речь шла про внешние зависимости для класса, причем такие, которые уже через интерфейсы работают. Киля предлагает в тестах использовать конкретные классы системы, реализующие эти интерфейсы. Я предлагаю замокать.

                          Эээ, про какие внешние зависимости? Речь шла про функционал. Я тебе пишу, что ты можешь встретить метод, который юзает другие сущности. Как это мокать, я не совсем понимаю. Придется просто сильно много мокать. А когда у тебя есть метод, который принимает два интерфейса, и использует исключительно только их - тогда конечно тут проблем не возникает.
                            Цитата Wound @
                            А когда у тебя есть метод, который принимает два интерфейса, и использует исключительно только их - тогда конечно тут проблем не возникает.

                            Но ты именно такой пример и приводил. У тебя было два интерфейса, но ты туда хотел подпихивать конкретные сущности.
                              Цитата OpenGL @
                              И просто юзать в приложении "официальный" bar.cpp, а в тестах bar_mocked.cpp?
                              Если очень кратко, то да. Но вообще-то нет. Это как-то слишком искусственно и неудобно.
                              Сообщение отредактировано: Qraizer -
                                Цитата Qraizer @
                                Если очень кратко, то да. Но вообще-то нет. Это как-то слишком искусственно и неудобно.

                                Тогда как конкретно это делать?
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0518 ]   [ 16 queries used ]   [ Generated: 18.04.24, 00:23 GMT ]