Badanie jednostkowe z użyciem singletonów
Przygotowałem kilka automatycznych testów z Visual Studio Team Edition testing framework. Chcę, aby jeden z testów połączył się z bazą danych w normalny sposób w Programie:
string r_providerName = ConfigurationManager.ConnectionStrings["main_db"].ProviderName;
Ale otrzymuję wyjątek w tej linii. Przypuszczam, że dzieje się tak, ponieważ ConfigurationManager to singleton. Jak obejść problem Singletona z testami jednostkowymi?
Dzięki za odpowiedzi. Wszystkie z nich były bardzo pouczające.
4 answers
Zajrzyj na Blog testujący Google :
- używanie iniekcji zależności w celu uniknięcia singletonów
- Singletony są patologicznymi kłamcami
- przyczyny singletonów
- gdzie się podziały wszystkie Singletony?
- Clean Code Talks - Global State and Singletons
- Dependency Injection .
A także:
W końcu Misko napisał na swoim blogu poradnik: pisanie kodu Testowalnego .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-18 12:36:23
Możesz użyć konstruktora dependency injection. Przykład:
public class SingletonDependedClass
{
private string _ProviderName;
public SingletonDependedClass()
: this(ConfigurationManager.ConnectionStrings["main_db"].ProviderName)
{
}
public SingletonDependedClass(string providerName)
{
_ProviderName = providerName;
}
}
Który pozwala przekazać łańcuch połączeń bezpośrednio do obiektu podczas testowania.
Również jeśli używasz Visual Studio Team Edition testing framework możesz uczynić konstruktor z parametrem prywatnym i przetestować klasę za pośrednictwem accesora.
Właściwie rozwiązuję tego typu problemy z wyśmiewaniem. Przykład:Masz klasę, która zależy od Singletona:
public class Singleton
{
public virtual string SomeProperty { get; set; }
private static Singleton _Instance;
public static Singleton Insatnce
{
get
{
if (_Instance == null)
{
_Instance = new Singleton();
}
return _Instance;
}
}
protected Singleton()
{
}
}
public class SingletonDependedClass
{
public void SomeMethod()
{
...
string str = Singleton.Insatnce.SomeProperty;
...
}
}
Po pierwsze SingletonDependedClass
musi być refactored to take Singleton
instance as constructor parameter:
public class SingletonDependedClass
{
private Singleton _SingletonInstance;
public SingletonDependedClass()
: this(Singleton.Insatnce)
{
}
private SingletonDependedClass(Singleton singletonInstance)
{
_SingletonInstance = singletonInstance;
}
public void SomeMethod()
{
string str = _SingletonInstance.SomeProperty;
}
}
Test SingletonDependedClass
(biblioteka MOQ jest używana):
[TestMethod()]
public void SomeMethodTest()
{
var singletonMock = new Mock<Singleton>();
singletonMock.Setup(s => s.SomeProperty).Returns("some test data");
var target = new SingletonDependedClass_Accessor(singletonMock.Object);
...
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-18 13:16:58
Przykład z książki: efektywna praca z kodem starszym
Tutaj też udzielono tej samej odpowiedzi: https://stackoverflow.com/a/28613595/929902
Aby uruchomić kod zawierający singletony w wiązce testowej, musimy rozluźnić właściwość singleton. Zrobimy tak. Pierwszym krokiem jest dodanie nowej statycznej metody do klasy singleton. Metoda pozwala nam zastąpić statyczną instancję w singletonie. Nazwiemy to setTestingInstance .
public class PermitRepository
{
private static PermitRepository instance = null;
private PermitRepository() {}
public static void setTestingInstance(PermitRepository newInstance)
{
instance = newInstance;
}
public static PermitRepository getInstance()
{
if (instance == null) {
instance = new PermitRepository();
}
return instance;
}
public Permit findAssociatedPermit(PermitNotice notice) {
...
}
...
}
Teraz, gdy mamy ten setter, możemy utworzyć testową instancję PermitRepository i ustawić go. Chcielibyśmy napisać taki kod w naszej konfiguracji testowej:
public void setUp() {
PermitRepository repository = PermitRepository.getInstance();
...
// add permits to the repository here
...
PermitRepository.setTestingInstance(repository);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:43
Masz tu do czynienia z bardziej ogólnym problemem. W przypadku niewłaściwego użycia Singletony utrudniają testabiliy.
Wykonałem szczegółową analizę tego problemu w kontekście niezwiązanego projektu. Postaram się podsumować moje punkty:
- Jeśli Twój Singleton ma znaczący stan globalny, nie używaj Singletona. Obejmuje to trwałe przechowywanie, takie jak bazy danych, pliki itp.
- w przypadkach, gdy zależność od obiektu Singleton nie jest oczywista przez nazwę klasy, zależność powinna być wstrzykiwana. Potrzeba wprowadzania instancji Singletona do klas dowodzi niewłaściwego użycia wzorca (patrz punkt 1).
- zakłada się, że cykl życia Singletona jest taki sam jak w przypadku aplikacji. Większość implementacji Singletona używa mechanizmu lazy-load do tworzenia instancji. Jest to trywialne i ich cykl życia jest mało prawdopodobne, aby zmienić, lub nie należy używać Singleton.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-18 12:56:27