Friday, August 27, 2010

Testable Singleton Clients

Вчера послушал 19й Подкаст Петербургской Группы Alt.Net. Ребята так сильно пинали несчастный Singleton, что я решил написать пару слов в его защиту. Одним из недостатков синглтона назвали то, что очень трудно, если вообще возможно, писать юнит тесты для классов использующих синглтон. Я попробую опровергнуть это утверждение.

Давайте рассмотрим простенький пример. У нас есть класс TextProvider, который имплементит интерфейс ITextProvider и при этом является синглтоном.

public interface ITextProvider
{
    string GetText();
}

public sealed class TextProvider : ITextProvider
{
    private static readonly ITextProvider _instance = new TextProvider();

    private TextProvider()
    { }

    public static ITextProvider Instance
    {
        get { return _instance; }
    }

    public string GetText()
    {
        return "Hello from Singleton!";
    }
}

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

public class Client
{
    private ITextProvider _textProvider;

    public Client(ITextProvider provider)
    {
        _textProvider = provider;
    }

    public void ConsumeText()
    {
        _textProvider.GetText();
    }
}

Теперь создать новый экземпляр клиента в коде можно вот так:

Client client = new Client(TextProvider.Instance);

А в тестах клиенту можно подсунуть замоканого провайдера:

ITextProvider provider = _mock.StrictMock<ITextProvider>();
Client client = new Client(provider);

2 comments: