С#: Unittesting с частными статическими членами?
У меня есть класс с такой конструкцией:
private static Dictionary<Contract, IPriceHistoryManager> _historyManagers = new Dictionary<Contract, IPriceHistoryManager>();
и скажем, 2 метода вроде:
public void AddSth()
{
_historManagers.Add(new Contract(), new PriceHistoryManager());
}
public int CountDic()
{
return _historyManagers.Count();
}
Проблема:
При запуске unittests нет слова "reset" в словаре и когда я создаю несколько unittests с отдельными экземплярами класса, тогда "CountDic" дает непредсказуемые результаты, и я не могу протестировать список.
Вопрос:
Это вообще считается "плохим" подходом, и если да: как сделать это лучше/более unittestable?
А если нет: как это сделать лучше всего?
спасибо.
Ответы
Ответ 1
Не бойтесь выставлять публичные операции для целей тестирования. Перефразируемый от "Искусства модульного тестирования" Роя Ошероу: Когда Toyota строит автомобиль, есть доступные пункты тестирования. Когда Intel строит чип, есть точки тестирования. Есть интерфейсы к автомобилю или чипу, которые существуют только для тестирования. Почему мы не делаем то же самое для программного обеспечения? Может ли метод ResetHistory()
полностью уничтожить ваш API?
Если этот ответ yes
, тогда создайте метод, но сделайте метод internal
. Затем вы можете использовать сборку InternalsVisibleTo
, чтобы разоблачить кишки в вашей библиотеке unit test. У вас есть метод, доступный для вас, созданный на 100% для тестирования, но никаких изменений в вашем публичном API нет.
Ответ 2
В вашем примере CountDic
не является непредсказуемым: он должен вернуть еще один, чем перед вызовом AddSth()
.
Итак:
[Test]
public void Test()
{
var item = new ClassUnderTest();
int initialCount = item.CountDic();
item.AddSth();
int finalCount = item.CountDic();
Assert.That(finalCount == initialCount + 1);
}
В целом, однако, тестирование классов, поддерживающих состояние, может быть сложным. Иногда необходимо разбить часть класса, поддерживающего состояние (в вашем случае, словарь) и переместить его в другой класс. Затем вы можете издеваться над этим классом "storage" и передать его через конструктор.