Comments 6
Для тестирования такого кода стоит использовать публичные методы класса. Юнит-тест – это такой же клиент основного кода, как и остальные, для него не должно быть привилегий дополнительного доступа к тестируемой системе по сравнению с обычным клиентским кодом.
Скорее наоборот.
Возможность доступа к приватным полям и методам, крайне упрощает тестирование.
Есть, конечно, BlackBox тестирование, но его польза сильно ограничена.
Про минусы такого подхода также было сказано - хрупкость тестов. Стоит взвесить плюсы и минусы.
Хрупкость тестов - это да. Но если Вы имеете в виду, что не стоит выставлять что-то наружу для всех только ради тестов, то это мудрая мысль. Публичный контракт - серьезное обязательство, а отказ от него сильно расстраивает пользователей, даже если делается по всем правилам этикета с предупреждениями obsolete в переходных версиях. Товарищ в коментарии ниже совершенно прав. Но существуют приемы открытия чего-то только для тестов. Вот есть класс:
public class Subject
{
private int value;
public Subject(int value) => this.value = value;
}
А нужен тривиальный тест, проверяющий присвоение значения полю в конструкторе. Тогда поле делаем видимым для наследника, а в тестопригодном наследнике выставляем наружу:
public class Subject
{
private int value;
// exposing a private field to subclasses
// is less harm than making it public
protected int Value => value;
public Subject(int value) => this.value = value;
}
public class TestableSubject : Subject
{
// transitively grant client code (i.e.test)
// access to the private field
public new int Value => base.Value;
public TestableSubject(int value) : base(value) { }
}
И тестируем спокойно:
[Test]
public void NormalTest()
{
// Arrange
int value = 42;
// Act
var sut = new TestableSubject(value);
// Assert
Assert.AreEqual(value, sut.Value);
}
Правда, такой фокус потом любой пользователь библиотеки повторить сможет.
У .NET есть InternalsVisibleToAttribute и вообще рефлексия:
public class Subject
{
private int value;
public Subject(int value) => this.value = value;
}
public class TestableSubject : Subject
{
public int Value => (int)typeof(Subject).GetField("value",
BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(this);
public TestableSubject(int value) : base(value) { }
}
Еще для .NET есть штуки вроде этой, которые могут патчить код после компиляции. Клиенты получают оригинальную библиотеку, а тесты патченную с открытым доступом к чему угодно. Думаю, для других платформ аналоги тоже есть.
Хрупкость - понятие относительное.
Скажем, модульные тесты непосредственно связаны с тестируемым кодом. И хозяин у рабочего кода и модельных тестов, как правило один. И жизненный цикл тестов и рабочего кода подчинен одинаковым приоритетам.
В этом случае, как таковой хрупкости нет.
Тут чситый закон Мерфи србатывает: если вытащить что-то в публичный API, то его сразу же подхватять и потащат по своему коду и нельзя будет договорится не использовать его. И это будет не тот случай, когда нужно изменить API, потому что это нужно людям. Люди странные и дуамют странно. А гряный API это как детей ругатся учить - тебя же потом и обругают.
Тут стоит пояснить, что я имел ввиду под "возможностью доступа к приватным полям и методам". Очевидно, этот доступ нужен без создания публичных методов только для целей тестирования.
Т.е. полезно для тестов иметь доступ к действительно приватным методам и полям.
Принципы юнит-тестирования. Часть вторая