dependency injection и простые аргументы
От: UberPsychoSvin  
Дата: 21.11.13 12:52
Оценка: -1 :)
Есть дао классы которые кушают конекшен стринг, это удобно, т.к. они используются изолированно.

var kernel = new StandardKernel();
//прописываем биндинги
var dao = kernel.Get<DaoCfg>();//во время конструирования кернела вытаскиваем объект
var cfg = doc.GetConfig();//берём конфиг с конекшен стрингом.
kernel.Bind<DaoBlaBlaBla>().WithConstructorArgs("cs", cfg.Cs);

А теперь вопрос, как мне написать, например, тест на то, что у меня приложение собирается. Как мне инициализировать StandardKernel так, что бы я мог мокнуть всё что угодно?
Re: dependency injection и простые аргументы
От: Sinix  
Дата: 21.11.13 13:16
Оценка:
Здравствуйте, UberPsychoSvin, Вы писали:

UPS>А теперь вопрос, как мне написать, например, тест на то, что у меня приложение собирается. Как мне инициализировать StandardKernel так, что бы я мог мокнуть всё что угодно?


Если нужно проверить корректность работы самого DI — юнит тестами на наборе типовых сценариев.
Если проверяется корректность настройки — в итоге рано или поздно придётся заниматься расстановкой ассертов и автоматизацией готового бинарника. Иначе не отловить самые гадкие ошибки "забыли конфиг-файл", "неправильная схема БД", "компонент а перезаписывает биндинг компонента б", "опечатка в инсталляторе" и т.д и т.п. Не, самые типовые вещи можно отловить отдельными тестами, но гарантировать что всё будет работать в сборе можно только если проверять всё в сборе
Re[2]: dependency injection и простые аргументы
От: UberPsychoSvin  
Дата: 22.11.13 06:21
Оценка:
Вообщем сделал конструкторы куда передаётся класс, который вытягивает этот конекшен стринг. В итоге могу мокнуть что угодно, т.к. на этапе конфигурации StandardKernel, не идёт никакого взаимодействия с окружением.
public class DoaBlaBla
{
    public DaoBlaBla(ICsResolver resolver) : base(resolver.Resolve()){}    
    public DaoBlaBla(string cs) base(cs) {}
}


Здравствуйте, Sinix, Вы писали:
S>Если проверяется корректность настройки — в итоге рано или поздно придётся заниматься расстановкой ассертов и автоматизацией готового бинарника. Иначе не отловить самые гадкие ошибки "забыли конфиг-файл", "неправильная схема БД", "компонент а перезаписывает биндинг компонента б", "опечатка в инсталляторе" и т.д и т.п. Не, самые типовые вещи можно отловить отдельными тестами, но гарантировать что всё будет работать в сборе можно только если проверять всё в сборе
Имхо юнит тесты должны быть максимально простыми, а то их замучаешься потом переписывать, когда нужно будет менять поведение кода, который они тестируют. А так и проверка всего в сборе может не сработать, если баг проявляется на специфической конфигурации.
Re[3]: dependency injection и простые аргументы
От: Sinix  
Дата: 22.11.13 07:51
Оценка:
Здравствуйте, UberPsychoSvin, Вы писали:

UPS>Вообщем сделал конструкторы куда передаётся класс, который вытягивает этот конекшен стринг. В итоге могу мокнуть что угодно, т.к. на этапе конфигурации StandardKernel, не идёт никакого взаимодействия с окружением.


Ок, а что этот тест проверяет — что DoaBlaBla с заданной строкой соединения работает правильно?
Тогда да, конструктор с строкой — самый простой и явный способ (на мой взгляд).

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


Угу. Лично я вообще предпочитаю тестировать юнит-тестами только инфраструктурный код и отдельные примитивы в бизнес-логике, и то тесты ограничиваются самыми типовыми сценариями использования (грубо говоря, покрывают контракт каждого отдельного куска). Код покрыт ассертами, которые сразу обнаруживают нарушение контракта, поэтому перебирать все мыслимые/немыслимые комбинации параметров не требуется.

Главный бонус — ассерты работают всегда, поэтому если ошибка проползла через юнит-тесты, она вылезет на интеграционных или при автоматизированном UI-тестировании.

В продакшне, разумеется, ассерты отключены, или пишут в лог.
Re[4]: dependency injection и простые аргументы
От: UberPsychoSvin  
Дата: 22.11.13 10:17
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, UberPsychoSvin, Вы писали:


UPS>>Вообщем сделал конструкторы куда передаётся класс, который вытягивает этот конекшен стринг. В итоге могу мокнуть что угодно, т.к. на этапе конфигурации StandardKernel, не идёт никакого взаимодействия с окружением.


S>Ок, а что этот тест проверяет — что DoaBlaBla с заданной строкой соединения работает правильно?

Вообще это был тест на то, что приложение собирается из зависимостей. А то у меня как то было, что пишу пишут пишу, а потом запускаю и вижу что у меня циклическая, блин, зависимость. + есть возможность мокнуть что угодно. Удобно для тестирования функционала, для которого нужны специальные условия, которые хрен воспроизведёшь.

S>Угу. Лично я вообще предпочитаю тестировать юнит-тестами только инфраструктурный код и отдельные примитивы в бизнес-логике, и то тесты ограничиваются самыми типовыми сценариями использования (грубо говоря, покрывают контракт каждого отдельного куска). Код покрыт ассертами, которые сразу обнаруживают нарушение контракта, поэтому перебирать все мыслимые/немыслимые комбинации параметров не требуется.


S>Главный бонус — ассерты работают всегда, поэтому если ошибка проползла через юнит-тесты, она вылезет на интеграционных или при автоматизированном UI-тестировании.


S>В продакшне, разумеется, ассерты отключены, или пишут в лог.

Я аналогично кидаю эксцепшены, если во входных данных методов какой то явный бред.
Re[5]: dependency injection и простые аргументы
От: Sinix  
Дата: 22.11.13 10:57
Оценка: 6 (1)
Здравствуйте, UberPsychoSvin, Вы писали:

S>>Ок, а что этот тест проверяет — что DoaBlaBla с заданной строкой соединения работает правильно?

UPS>Вообще это был тест на то, что приложение собирается из зависимостей. А то у меня как то было, что пишу пишут пишу, а потом запускаю и вижу что у меня циклическая, блин, зависимость. + есть возможность мокнуть что угодно. Удобно для тестирования функционала, для которого нужны специальные условия, которые хрен воспроизведёшь.

Тогда я в лёгкой растерянности: проверяем, что все зависимости расставлены правильно, но при этом убираем саму передачу зависимостей?


S>>В продакшне, разумеется, ассерты отключены, или пишут в лог.

UPS>Я аналогично кидаю эксцепшены, если во входных данных методов какой то явный бред.
Не, эксепшны — это совсем не то.

Во-первых, они захламляют код и вынуждают копировать типовые проверки. Ну вот сравни:
    if (arg == null)
        throw new ArgumentNullException("arg", "SomeMessage");
    if (arg2 == arg)
        throw new ArgumentException("SomeMessage 2", "arg2"); // Угу, порядок параметров разный. Убил бы:(
    if (this.lastArgField > arg2)
        throw new InvalidOperationException("SomeMessage 3");
    // ...

    // и
    Code.NotNull(arg, "arg", "SomeMessage");
    Code.AssertArgument(arg2 != arg, "arg2", "SomeMessage 2");
    Code.AssertState(this.lastArgField <= arg2, "SomeMessage 3");

Особенно шикарно выглядят if-then-throw, если стиль кода требует фигурных скобок даже для однострочных if.

Что проще поддерживать и переиспользовать? Особенно если нужно поддерживать не только проверки параметров, но и прочие исключения, или если проверки в основном сложнее чем != null?


Во-вторых, ассерты офигенно настраиваемая штука. В зависимости от настройки сборки (или настроек в конфиг-файлах) Code.NotNull(); может:

* Не делать ничего.
* При подключенном отладчике немедленно прерывать выполнение прямо в точке где нарушается ассерт (а не внутри ассерта) и не пускать дальше, пока не поправишь ошибку. Вместе с Edit-and-continue офигенно удобная штука.
* Бросать исключение. Опционально — вместо ArgumentNullEException будет бросаться приватный наследник System.Exception, чтобы обламывать попытки "заглушить" нарушение контракта catch-ем.
* Писать в лог.

Последнее вместе с хелперами вида
using (Code.BeginScope("123")) // Никто не запрещает разнести код для трассировки и ассертов в разные классы, упростил для краткости.
{
  using (Code.BeginScope("1234"))
  {
    // Some code
    Code.Info("...")
  }
}


может генерить лог с учётом вложенноcти операций/параллельного их выполнения. На выходе имеем xml вида
<Op Text="123" Time="..." Stack="...">
 <Op Text="123\1234" Time="..." Stack="...">
   <Info>...</Info>
   <OpCompleted Time="..." />
 </Op>
 <OpCompleted Time="..." />
</Op>

, причём xml может собираться в фоне, не сильно влияя на основной поток. При условии сбора статистики и автоматического анализа — бесценная штука для поиска регрессий, в том числе и по производительности.


В-третьих никто не мешает заводить несколько классов для ассертов и помечать их [Conditional]. Например,
    Code.NotNull(arg, "arg", "SomeMessage");
    // Всё что ниже - компилируется только для отладочных сборок.
    DebugCode.AssertArgument(arg2 != arg, "arg2", "SomeMessage 2");
    DebugCode.AssertState(SomeDetailedCheck(), "SomeMessage 3");


В-четвёртых (для сложных систем) никто не запрещает распространять класс для обработки ассертов через DI и наворачивать самые безумные сочетания для отдельных частей приложения.

В-пятых, ассерты можно чуть подтюнить для их автоматической верификации в CodeContracts. По всему бизнес-коду так сделать не выйдет — слишком трудоёмкая задача. Но для отдельных критичных частей — почему нет?

Так что эксепшны фигня если сравнивать их с правильно приготовленными ассертами
Re[6]: dependency injection и простые аргументы
От: UberPsychoSvin  
Дата: 22.11.13 11:38
Оценка: 12 (1)
Sinix
Да не, я зависимоти не убирал, я из процесса настройки зависимостей(где все биндинги прописываются) убрал работу с ресурсами, что бы вытаскивать конекшен стринг. теперь у меня все дао классы, зависят не только от конкретного конекшен стринга, но и от интерфейса ICsResolver. Блин, сложно описывать код на словах.

На счёт ассертов, по идее местами полезная фича, если ассерт в дебаге вместо эксцепшена будет вызывать отладчик, а в релизе кидаться эксцепшенами. Вот только в .NET овских assert, я не вижу что бы был выбор типа эксцепшена.

А Code.BeginScope("123") это StopWatch такой на стероидах?
Re[7]: dependency injection и простые аргументы
От: Sinix  
Дата: 22.11.13 11:55
Оценка:
Здравствуйте, UberPsychoSvin, Вы писали:

UPS>На счёт ассертов, по идее местами полезная фича, если ассерт в дебаге вместо эксцепшена будет вызывать отладчик, а в релизе кидаться эксцепшенами. Вот только в .NET овских assert, я не вижу что бы был выбор типа эксцепшена.


Угу, свой велосипед за несколько лет настрогался По умолчанию поведение чуть хитрее: если подключен отладчик — прерываем выполнение, не подключен — кидаем исключение.

UPS>А Code.BeginScope("123") это StopWatch такой на стероидах?

Не, там важен не столько stopwatch, сколько запись в лог через фоновый поток + отслеживание вложенности операций. BeginScope() делали на прошлой работе, по памяти там под капотом был обычный TraceSource + отслеживание стека через CorrelationManager, но это не принципиально — при желании можно поменять поведение на своё.
Re[6]: что такое " приватный наследник System.Exception"
От: Аноним  
Дата: 27.11.13 08:12
Оценка:
сабж?
Re[7]: что такое " приватный наследник System.Exception"
От: Sinix  
Дата: 27.11.13 09:27
Оценка:
Здравствуйте, Аноним, Вы писали:

А>сабж?


Такие вещи нужны очень редко, в основном — в биз-критикал-коде, который не должны вызывать с нарушениями контракта. Как оказалось, иногда вызывают. Причём с try-catch, чтобы обойти тесты

Как пример,
    class Asertions
    {
        // Assertions.dll
        [Serializable]
        private class FailException : Exception
        {
            // ...
        }

        public void Assertion()
        {
            // ...
            throw new FailException();
        }
    }

    // ...

        void MethodA()
        {
            Assertions.Assertion();
        }

        void MethodB()
        {
            try
            {
                MethodA();
            }
            catch (FailException ex) // неа.
            {
            }
        }


В итоге MethodB сможет поймать FailException только через catch(System.Exception), что уже отлавливается статическими анализаторами. Практика распространённая, например, в CodeContracts делают нечто похожее.
Re[6]: dependency injection и простые аргументы
От: SergASh  
Дата: 03.06.15 17:45
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Во-вторых, ассерты офигенно настраиваемая штука. В зависимости от настройки сборки (или настроек в конфиг-файлах) Code.NotNull(); может:


S>* Не делать ничего.

S>* При подключенном отладчике немедленно прерывать выполнение прямо в точке где нарушается ассерт (а не внутри ассерта) и не пускать дальше, пока не поправишь ошибку. Вместе с Edit-and-continue офигенно удобная штука.
S>* Бросать исключение. Опционально — вместо ArgumentNullEException будет бросаться приватный наследник System.Exception, чтобы обламывать попытки "заглушить" нарушение контракта catch-ем.
S>* Писать в лог.

А можно поподробнее как вы это делаете? Вроде ж Debugger.Break() всегда прерывается там, где он написан. Или используется какая-то хитрая магия?

PS. Знаю, что пост старый. Про ассерты как-то на удивление мало толкового написано.
Re[7]: dependency injection и простые аргументы
От: Sinix  
Дата: 03.06.15 19:21
Оценка:
Здравствуйте, SergASh, Вы писали:

SAS>А можно поподробнее как вы это делаете? Вроде ж Debugger.Break() всегда прерывается там, где он написан. Или используется какая-то хитрая магия?

Очень просто — написан целиком свой код для ассертов со всеми нужными и ненужными плюшками. Общую схему, если интересно, могу рассказать.

SAS>PS. Знаю, что пост старый. Про ассерты как-то на удивление мало толкового написано.

Угу. Потому что ассерты в дотнете из коробки использовать очень неудобно, плюс ассерты немного задавлены модой на юнит-тестирование (хотя по идее это перепендикулярные вещи, одно другого не отменяет).
Re[8]: dependency injection и простые аргументы
От: SergASh  
Дата: 04.06.15 12:00
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, SergASh, Вы писали:


SAS>>А можно поподробнее как вы это делаете? Вроде ж Debugger.Break() всегда прерывается там, где он написан. Или используется какая-то хитрая магия?

S>Очень просто — написан целиком свой код для ассертов со всеми нужными и ненужными плюшками. Общую схему, если интересно, могу рассказать.

Интересно не то слово, рассказывайте!

SAS>>PS. Знаю, что пост старый. Про ассерты как-то на удивление мало толкового написано.

S>Угу. Потому что ассерты в дотнете из коробки использовать очень неудобно, плюс ассерты немного задавлены модой на юнит-тестирование (хотя по идее это перепендикулярные вещи, одно другого не отменяет).

Тут пришло осознание, что явные проверки всех условий посреди кода никудышне его документируют, не говоря уже о том, что код распухает от if/throw на двух строчках.
Стандартные ассерты мало на что годятся, согласен. Увы, со времен когда Дж.Роббинс написал свою книгу про отладку ничего особо не изменилось.
Так что тоже пытаюсь написать свои.
Re[9]: dependency injection и простые аргументы
От: Sinix  
Дата: 04.06.15 14:26
Оценка:
Здравствуйте, SergASh, Вы писали:

SAS>Интересно не то слово, рассказывайте!

Некропостинг, так что отдельной веткой
Автор: Sinix
Дата: 04.06.15
.

SAS>Тут пришло осознание, что явные проверки всех условий посреди кода никудышне его документируют, не говоря уже о том, что код распухает от if/throw на двух строчках.

Это отдельная проблема, пожже допишу в той же ветке
Re[2]: dependency injection и простые аргументы
От: itslave СССР  
Дата: 04.06.15 14:59
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, UberPsychoSvin, Вы писали:


UPS>>А теперь вопрос, как мне написать, например, тест на то, что у меня приложение собирается. Как мне инициализировать StandardKernel так, что бы я мог мокнуть всё что угодно?


S>Если нужно проверить корректность работы самого DI — юнит тестами на наборе типовых сценариев.

Дзен юнит тестирования — тестить только свой кожд и не тестить 3rd party components и инфраструктуру. Поэтому конфигурацию DI надо тестить только интегрейшн тестами.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.