Есть синглетон и юнит тесты к нему.
Каждый тест проверяет какую либо часть класса синглетона, косвенно предполагая что синглтон создался перед тестом.
Когда тесты выполняются путем индивидуального вызова каждого теста — все нормально.
Однако когда тесты выполняются в группе, происходят различные накладки связанные с изменением статуса синглтона предыдущими тестами.
Рещить проблему довольно просто добавив в синглтон метод "Установить первоначальное состояние", однако этот метод требуется исключительно для тестирования, в реальном использовании его нельзя никогда вызывать.
Можно еще запускать каждый тест всегда в отдельном потоке, но неясно как это сделать красиво, к чему это приведет при тестировании, да и нужно будет для всего проекта несколько сотен тестов править.
Главная дилемма:править код ради тестов или извращаться с тестами?
Здравствуйте, AlexNek, Вы писали:
AN>Есть синглетон и юнит тесты к нему. AN>Каждый тест проверяет какую либо часть класса синглетона, косвенно предполагая что синглтон создался перед тестом. AN>Когда тесты выполняются путем индивидуального вызова каждого теста — все нормально. AN>Однако когда тесты выполняются в группе, происходят различные накладки связанные с изменением статуса синглтона предыдущими тестами. AN>Рещить проблему довольно просто добавив в синглтон метод "Установить первоначальное состояние", однако этот метод требуется исключительно для тестирования, в реальном использовании его нельзя никогда вызывать. AN>Можно еще запускать каждый тест всегда в отдельном потоке, но неясно как это сделать красиво, к чему это приведет при тестировании, да и нужно будет для всего проекта несколько сотен тестов править. AN>Главная дилемма:править код ради тестов или извращаться с тестами?
Можно поместить синглтон в динамическую библиотеку, а затем вызывать LoadLibrary/TestSingleton/FreeLibrary.
Можно сделать пару шаблонных методов setup/teardown специально для тестов — поскольку они
шаблонные, то в релиз не попадут, ибо вызываться не будут.
Кстати, а почему тестируется именно синглтон, а не сам объект, который "синглтонится" ?
И не стоит ли их разделить и тестировать по отдельности ?
AN>Главная дилемма:править код ради тестов или извращаться с тестами?
Стандартый способ — это разработка синглетона, как обычного instance-класса, который "замыкается в синглетон-переменную".
Это позволяет один и тот же код, использовать и как singleton, и как не singleton.
И в тестах поведение синглетона тестируется так же, как тестируется обычный класс, а не как синглетон.
Здравствуйте, dimgel, Вы писали:
D>Здравствуйте, AlexNek,
D>Я на скале (там объект-синглтон отличается от класса ключевым словом — "object" вместо "class"; для "object C" неявно создаётся класс C$) делаю так:
D>
D>class C protected(dependencies) {
D> ...
D>}
D>object C extends C(default-runtime-dependencies)
D>
D>А для тестов создаю экземпляр так:
D>
D>new C(mock-dependencies)
D>
К сожалению, требуется для С# (NET 2.0), да и отделять класс от синглтона совсем не хочется (хотя класс просто наследуется от "синглтон темплейт"), тогда не будут работать "стандартные вызовы" используемые в программе и тестах.
Здравствуйте, AlexNek, Вы писали:
AN>Главная дилемма:править код ради тестов или извращаться с тестами?
Единственный нормальный способ — выкинуть синглтон нафик. Если же есть желание поизвращаться и речь про дотнет — создавай на каждый тест отдельный аппдомен.
... << RSDN@Home 1.2.0 alpha 5 rev. 27 on Windows 7 6.1.7601.65536>>
Здравствуйте, okman, Вы писали:
O>Здравствуйте, AlexNek, Вы писали:
AN>>Есть синглетон и юнит тесты к нему. AN>>Каждый тест проверяет какую либо часть класса синглетона, косвенно предполагая что синглтон создался перед тестом. AN>>Когда тесты выполняются путем индивидуального вызова каждого теста — все нормально. AN>>Однако когда тесты выполняются в группе, происходят различные накладки связанные с изменением статуса синглтона предыдущими тестами. AN>>Рещить проблему довольно просто добавив в синглтон метод "Установить первоначальное состояние", однако этот метод требуется исключительно для тестирования, в реальном использовании его нельзя никогда вызывать. AN>>Можно еще запускать каждый тест всегда в отдельном потоке, но неясно как это сделать красиво, к чему это приведет при тестировании, да и нужно будет для всего проекта несколько сотен тестов править. AN>>Главная дилемма:править код ради тестов или извращаться с тестами?
O>Можно поместить синглтон в динамическую библиотеку, а затем вызывать LoadLibrary/TestSingleton/FreeLibrary. O>Можно сделать пару шаблонных методов setup/teardown специально для тестов
Дя шарпа generic функция с фиктивным параметром? Можно ее еще интернал сделать — пожалуй подойдет. O>- поскольку они шаблонные, то в релиз не попадут, ибо вызываться не будут.
O>Кстати, а почему тестируется именно синглтон, а не сам объект, который "синглтонится" ?
Тут видимо различия в терминологии и краткости описания. Речь идет о реальном объекте который подчиняется концепту синглтона. O>И не стоит ли их разделить и тестировать по отдельности ?
Реальный класс просто наследуется от шаблона синглтона. Так что в принципе можно было бы разделить. Но как потом запретить использовать класс "просто так". Да и как потом тестировать другие классы использующие данный синглтон? Вполне может быть идентичная проблема.
Здравствуйте, DarkGray, Вы писали:
AN>>Главная дилемма:править код ради тестов или извращаться с тестами?
DG>Стандартый способ — это разработка синглетона, как обычного instance-класса, который "замыкается в синглетон-переменную".
Что то не вижу "красивого решения", можно примерчик? В итоге требуется для шарпа, но для начала подойдет любой язык без "встроенного синглтона" DG>Это позволяет один и тот же код, использовать и как singleton, и как не singleton.
Как это разрешить исключительно для тестов?
DG>>Стандартый способ — это разработка синглетона, как обычного instance-класса, который "замыкается в синглетон-переменную". AN>Что то не вижу "красивого решения", можно примерчик? В итоге требуется для шарпа, но для начала подойдет любой язык без "встроенного синглтона"
class SingletonClass
{
public void Method1(){}
public static readonly SingletonClass Singleton = new SingletonClass();
}
использование как Singleton — SingletonClass.Singleton.Method1();
использование не как Singleton — new SingletonClass().Method1();
DG>>Это позволяет один и тот же код, использовать и как singleton, и как не singleton. AN>Как это разрешить исключительно для тестов?
сделать конструктор SingletonClass-а protected, и в тестовой сборке сделать вспомогательный метод для создания класса
class SingletonClass
{
protected SingletonClass(){}
..
}
SingletonClass CreateSingletonClass()
{
return new SingletonClass2();
}
class SingletonClass2:SingletonClass{}
Здравствуйте, AlexNek, Вы писали:
AN>Что то не вижу "красивого решения", можно примерчик? В итоге требуется для шарпа, но для начала подойдет любой язык без "встроенного синглтона"
Возможно, красивого решения именно для синглтона и не существует.
Не даром он часто преподносится, как антипаттерн. Ведь класс-синглтон выполняет, по сути,
две функции — "родные" функции класса и функции ограничивания количества экземпляров.
Поэтому и тестировать его в большинстве случаев неудобно.
Еще, IMHO, наследоваться от синглтона неверно. Ведь это класс-одиночка, а наследники
будут связаны с ним отношениями "is-a". То есть, derived class is a base class, что
противоречит назначению синглтона.
В общем, надо как-то извратиться и тестировать отдельно функции синглетона (как шаблона) и
функции класса, который упаковывается в синглтон (в Вашем случае — наследуется от шаблона
синглтона). То есть, писать тесты, которые тестируют одиночность класса и единственность
его инициализации (плюс, возможно, его потокобезопасность), и тесты, тестирующие сам класс.
Например в C++, имея стандартный синглтон Майерса и класс settings, подлежащий
вынесению в разряд одиночек, я бы написал тесты для шаблона singleton<>, юзающего
какой-нибудь специально созданный для этой цели mock-объект, проверяя единственность
экземпляра и инициализации, плюс, возможно, его потокобезопасность, а settings
тестировал отдельно, как обычный класс. Ну а потом набросал несколько тестов "на
интеграцию", то есть — использующих singleton<settings>. Как-то так, в общем.
Для .NET не в курсе, как там принято обходиться с синглтонами, но вообще отношение к
ним очень разное, от чуть ли не поклонения до беспощадной критики — http://rsdn.ru/forum/cpp/4652557.aspx
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, AlexNek, Вы писали:
AN>>Главная дилемма:править код ради тестов или извращаться с тестами?
AVK>Единственный нормальный способ — выкинуть синглтон нафик.
Нужно будет перелопачивать весь проект, не думаю что на это кто то пойдет. Но даже если и да, то что использовать взамен? Нужен объект к которому можно просто обратится из произвольного места программы. AVK>Если же есть желание поизвращаться и речь про дотнет — создавай на каждый тест отдельный аппдомен.
Да речь про .НЕТ, главное сделать это в стартапе.
O>Возможно, красивого решения именно для синглтона и не существует. O>Не даром он часто преподносится, как антипаттерн. Ведь класс-синглтон выполняет, по сути, O>две функции — "родные" функции класса и функции ограничивания количества экземпляров. O>Поэтому и тестировать его в большинстве случаев неудобно.
Скорее, вот по какой причине:
Число состояний синглтона = 2 в степени (количество внутренних переменных)
Число возможных последовательностей состояний = факториал от предыдущей величины.
Так что если у синглтона 10 внутренних переменных, то число тестов пропорционально факториалу от тысячи.
А если 20 переменных, то никакие тесты не спасут.
Здравствуйте, DarkGray, Вы писали:
DG>>>Стандартый способ — это разработка синглетона, как обычного instance-класса, который "замыкается в синглетон-переменную". AN>>Что то не вижу "красивого решения", можно примерчик? В итоге требуется для шарпа, но для начала подойдет любой язык без "встроенного синглтона"
DG>
DG>class SingletonClass
DG>{
DG> public void Method1(){}
DG> public static readonly SingletonClass Singleton = new SingletonClass();
DG>}
DG>
Здесь есть несколько моментов для нас:
— стояла задача простого превращения "любого" класса в синглтон одним и тем же универсальным способом, без модификации класса.
— нужна lazy instantiation.
Использум модифицированную 5 версию: Implementing the Singleton Pattern in C#
DG>использование как Singleton — SingletonClass.Singleton.Method1(); DG>использование не как Singleton — new SingletonClass().Method1();
DG>>>Это позволяет один и тот же код, использовать и как singleton, и как не singleton. AN>>Как это разрешить исключительно для тестов?
DG>сделать конструктор SingletonClass-а protected,
По идее так и должно быть DG>и в тестовой сборке сделать вспомогательный метод для создания класса
DG>
Здравствуйте, okman, Вы писали:
O>Здравствуйте, AlexNek, Вы писали:
AN>>Что то не вижу "красивого решения", можно примерчик? В итоге требуется для шарпа, но для начала подойдет любой язык без "встроенного синглтона"
O>Возможно, красивого решения именно для синглтона и не существует. O>Не даром он часто преподносится, как антипаттерн. Ведь класс-синглтон выполняет, по сути, O>две функции — "родные" функции класса и функции ограничивания количества экземпляров. O>Поэтому и тестировать его в большинстве случаев неудобно.
O>Еще, IMHO, наследоваться от синглтона неверно. Ведь это класс-одиночка, а наследники O>будут связаны с ним отношениями "is-a". То есть, derived class is a base class, что O>противоречит назначению синглтона.
"Наследование" выглядит следующим образом (более лучшего решения в то время не было найдено)
public class FrameworkManager : Singleton<FrameworkManager>, ISingletonInit
{
...
}
O>В общем, надо как-то извратиться и тестировать отдельно функции синглетона (как шаблона) и O>функции класса, который упаковывается в синглтон (в Вашем случае — наследуется от шаблона O>синглтона). То есть, писать тесты, которые тестируют одиночность класса и единственность O>его инициализации (плюс, возможно, его потокобезопасность), и тесты, тестирующие сам класс.
Это свободно можно разделить использую дополнительный тествый класс, так как все остальные классы буду вести себя одинаковым образом.
O>Например в C++, имея стандартный синглтон Майерса и класс settings, подлежащий O>вынесению в разряд одиночек, я бы написал тесты для шаблона singleton<>, юзающего O>какой-нибудь специально созданный для этой цели mock-объект, проверяя единственность O>экземпляра и инициализации, плюс, возможно, его потокобезопасность, а settings O>тестировал отдельно, как обычный класс. O>Ну а потом набросал несколько тестов "наинтеграцию", то есть — использующих singleton<settings>.
В этих-то тестах и проблема
O>Для .NET не в курсе, как там принято обходиться с синглтонами, но вообще отношение к O>ним очень разное, от чуть ли не поклонения до беспощадной критики — http://rsdn.ru/forum/cpp/4652557.aspx
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, AlexNek, Вы писали:
AVK>>>Единственный нормальный способ — выкинуть синглтон нафик. AN>>Нужно будет перелопачивать весь проект
AVK>Это полезно.
это если проект "для себя". Хотел бы я знать кто на работе позволит курочить рабочий проект "для красоты"
AN>> Но даже если и да, то что использовать взамен?
AVK>Явную передачу контекста.
То бишь, на всю иерархию всегда передавать чего то?