Как написать модульные тесты для подкласса с параметризованным базовым классом
Учитывая, что у меня есть класс в одной сборке, называемый GBase, с конструктором, который принимает 2 параметра, и подклассом GBase (назовем его GDerived), который принимает те же параметры, как мне разделить их, чтобы я мог выполнить модульное тестирование подкласса?
В другой сборке:
public class GBase
{
public GBase(ParamType1 param1, ParamType2 param2)
{
...
}
protected ParamType1 SomeProperty { get; set; }
// other stuff
}
В этой сборке:
public class GDerived : GBase
{
public GDerived(ParamType1 param1, ParamType2 param2)
:base(param1, param2)
{
// new code
SomeProperty = newCalculatedValue;
// other stuff
}
// other stuff
}
Исходный класс GBase - это устаревший код, как и общая структура программы - об изменении структуры не может быть и речи из-за размера кодовой базы (10 тыс. Строк плюс) - ни один из которых никогда не имел написанного для него модульного теста, пока совсем недавно.
Итак, теперь я хочу написать тест (используя NUnit) для конструктора подкласса, чтобы убедиться, что правильные свойства заполнены правильными значениями. Обратите внимание, что тестовые классы находятся в том же проекте, что и тестируемые классы.
[TestFixture]
public class GDerivedTests
{
[Test]
public void GDerivedConstructor_ValidParams_PropertiesSetCorrectly()
{
var newGDerived = new GDerived(parameter1, parameter2);
Assert.That(SomeProperty == parameter1;
}
}
Это очень грубое повторение того, с чем нам приходится иметь дело, и есть другие случаи, кроме установки свойства в базовом классе, который нам нужно протестировать. Я просто не знаю наверняка, с чего начать. У меня есть книга Майкла Фезерса "Эффективная работа с унаследованным кодом", но, похоже, она не охватывает этот широко распространенный "шаблон проектирования", широко используемый в коде, с которым мы имеем дело. Потому что это так просто, что любой мигающий иджйот должен знать, как с этим бороться, или это потому, что это редкий случай? Почему-то я тоже так не думаю, но могу ошибаться...
Один из возможных методов, о котором я подумал, - это извлечь интерфейс для базового класса и смоделировать конструктор базового класса, но я не уверен в деталях, как это сделать. Обратите внимание, что мы все относительные новички в модульном тестировании в команде, но у нас нет опыта. Не кодирование новичков, просто юнит-тестирование новичков.
ТИА, Дейв
1 ответ
Для начала: будь проще! В вашем примере единственное, что вы можете проверить, это SomeProperty
, Все остальное находится в базовом классе, который, как вам кажется, вы не хотите тестировать, поэтому тестовый метод GDerivedConstructor_ValidParams_PropertiesSetCorrectly()
не имеет смысла. В долгосрочной перспективе было бы разумно иметь тесты на это, хотя.
Тесты обычно содержат три элемента, известные как AAA: Arrange, Act и Assert. Итак, напишите свой тест так:
[Test]
public void GDerivedTestOfSomeProperty()
{
// arrange
ParamOfSomeProperty expected = ValueWeAreLookingFor; // this is something that you
// have in newCalculatedValue
// act
GDerived actual = new GDerived(
AnyValueThatMakesThisTestWork1, // maybe null?
AnyValueThatMakesThisTestWork2); // maybe null?
// assert
Assert.AreEqual(expected, actual.SomeProperty);
}
Вот и все для начала. Перейти отсюда. Вскоре вы увидите, что вы получаете много избыточного кода, так что вы, возможно, захотите через некоторое время переработать его.
Насмешка имеет смысл для тестирования базового класса или когда базовый класс делает какие-то странные вещи с объектами, которые вводятся. В этом случае передайте в mocks вместо реальных объектов. Лично я бы использовал фальшивый фреймворк, который сделает всю работу за вас, и вы также можете использовать его для тестирования самого базового класса. Известный пример - moq.
На заметку: вам будет лучше, если вы перенесете свои тестовые классы в собственный проект. Тестовый код не должен выпускаться по разным причинам, плюс сборка, тестирование и развертывание могут стать проще, если они разделены.