Единичное тестирование с одноточечными
Я подготовил несколько автоматических тестов с базой тестирования Visual Studio Team Edition. Я хочу, чтобы один из тестов подключался к базе данных, следуя нормальному пути, который он выполняет в программе:
string r_providerName = ConfigurationManager.ConnectionStrings["main_db"].ProviderName;
Но я получаю исключение в этой строке. Я предполагаю, что это происходит, потому что ConfigurationManager является одноэлементным. Как вы можете обойти проблему singleton с модульными тестами?
Спасибо за ответы. Все они очень поучительны.
Ответы
Ответ 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.