Подтвердить что ты не робот

Единичное тестирование с одноточечными

Я подготовил несколько автоматических тестов с базой тестирования Visual Studio Team Edition. Я хочу, чтобы один из тестов подключался к базе данных, следуя нормальному пути, который он выполняет в программе:

string r_providerName = ConfigurationManager.ConnectionStrings["main_db"].ProviderName;

Но я получаю исключение в этой строке. Я предполагаю, что это происходит, потому что ConfigurationManager является одноэлементным. Как вы можете обойти проблему singleton с модульными тестами?


Спасибо за ответы. Все они очень поучительны.

4b9b3361

Ответ 1

Посмотрите Блог Google Тестирование:

А также:

Наконец, Мишко Хевери написал руководство в своем блоге: Написание тестового кода.

Ответ 2

Вы можете использовать инъекцию зависимостей конструктора. Пример:

public class SingletonDependedClass
{
    private string _ProviderName;

    public SingletonDependedClass()
        : this(ConfigurationManager.ConnectionStrings["main_db"].ProviderName)
    {
    }

    public SingletonDependedClass(string providerName)
    {
        _ProviderName = providerName;
    }
}

Это позволяет передавать строку соединения непосредственно объекту во время тестирования.

Также, если вы используете среду тестирования Visual Studio Team Edition, вы можете сделать конструктор с параметром private и протестировать класс через accessor.

На самом деле я решаю такие проблемы с насмешкой. Пример:

У вас есть класс, который зависит от singleton:

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;
        ...
    }
}

Прежде всего SingletonDependedClass необходимо реорганизовать, чтобы взять экземпляр Singleton в качестве параметра конструктора:

public class SingletonDependedClass
{    
    private Singleton _SingletonInstance;

    public SingletonDependedClass()
        : this(Singleton.Insatnce)
    {
    }

    private SingletonDependedClass(Singleton singletonInstance)
    {
        _SingletonInstance = singletonInstance;
    }

    public void SomeMethod()
    {
        string str = _SingletonInstance.SomeProperty;
    }
}

Используется тест SingletonDependedClass (Mook mocking library):

[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);
    ...
}

Ответ 3

Пример из книги: Эффективная работа с устаревшим кодом

Также дается тот же ответ здесь: fooobar.com/questions/134748/...

Чтобы запустить код, содержащий синглтоны в тестовом жгуте, мы должны расслабить свойство singleton. Вот как мы это делаем. Первым шагом является добавление нового статического метода в одноэлементный класс. Метод позволяет заменить статический экземпляр в одноэлементном. Хорошо позвоните 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) {
    ...
    }
    ...
}

Теперь, когда у нас есть этот сеттер, мы можем создать тестовый экземпляр PermitRepository и установите его. Wed нравится писать код, подобный этому, в нашей тестовой настройке:

public void setUp() {
    PermitRepository repository = PermitRepository.getInstance();
    ...
    // add permits to the repository here
    ...
    PermitRepository.setTestingInstance(repository);
}

Ответ 4

Здесь вы столкнулись с более общей проблемой. При неправильном использовании синглтоны препятствуют тестированию.

Я сделал подробный анализ этой проблемы в контексте развязанного дизайна. Я постараюсь обобщить мои замечания:

  • Если ваш Singleton несет значительное глобальное состояние, не используйте Singleton. Это включает постоянное хранилище, такое как базы данных, файлы и т.д.
  • В случаях, когда зависимость от Singleton Object не является очевидной по названию классов, следует вводить зависимость. Необходимость введения экземпляров Singleton в классы доказывает неправильное использование шаблона (см. Пункт 1).
  • Предполагается, что жизненный цикл Singletones будет таким же, как и приложения. Большинство реализаций Singleton используют механизм ленивой нагрузки для создания экземпляров. Это тривиально, и их жизненный цикл вряд ли изменится, иначе вы не должны использовать Singleton.