unit-testing
Тестирование единиц: наилучшая практика
Поиск…
Вступление
Единичный тест - это самая маленькая тестируемая часть приложения, такая как функции, классы, процедуры, интерфейсы. Модульное тестирование - это метод, с помощью которого тестируются отдельные единицы исходного кода, чтобы определить, подходят ли они для использования. Модульные тесты в основном написаны и выполняются разработчиками программного обеспечения, чтобы убедиться, что код соответствует его дизайну и требованиям и ведет себя так, как ожидалось.
Хорошее название
Важность хорошего именования может быть лучше всего проиллюстрирована некоторыми плохими примерами:
[Test]
Test1() {...} //Cryptic name - absolutely no information
[Test]
TestFoo() {...} //Name of the function - and where can I find the expected behaviour?
[Test]
TestTFSid567843() {...} //Huh? You want me to lookup the context in the database?
Хорошие тесты нуждаются в хороших именах. Хороший тест не тестирует методы, тестовые сценарии или требования.
Хорошее именование также предоставляет информацию о контексте и ожидаемом поведении. В идеале, когда тест не выполняется на вашей машине сборки, вы должны решить, что не так, не глядя на тестовый код или даже сложнее, имея необходимость отлаживать его.
Хорошее именование позволяет вам время для чтения кода и отладки:
[Test]
public void GetOption_WithUnkownOption_ReturnsEmptyString() {...}
[Test]
public void GetOption_WithUnknownEmptyOption_ReturnsEmptyString() {...}
Для новичков может оказаться полезным запустить тестовое имя с помощью EnsureThat_ или аналогичного префикса. Начните с «EnsureThat_», чтобы начать думать о сценарии или требовании, требующем теста:
[Test]
public void EnsureThat_GetOption_WithUnkownOption_ReturnsEmptyString() {...}
[Test]
public void EnsureThat_GetOption_WithUnknownEmptyOption_ReturnsEmptyString() {...}
Именование важно для тестовых приборов. Назовите тестовое крепление после тестируемого класса:
[TestFixture]
public class OptionsTests //tests for class Options
{
...
}
Окончательный вывод:
Хорошее именование приводит к хорошим испытаниям, что приводит к хорошему дизайну в производственном коде.
От простого до сложного
То же самое, что и при написании классов - начните с простых случаев, затем добавьте требование (ака тесты) и реализацию (как производственный код) в каждом случае:
[Test]
public void EnsureThat_IsLeapYearIfDecimalMultipleOf4() {...}
[Test]
public void EnsureThat_IsNOTLeapYearIfDecimalMultipleOf100 {...}
[Test]
public void EnsureThat_IsLeapYearIfDecimalMultipleOf400 {...}
Не забудьте шаг рефакторинга, когда закончите с требованиями - сначала реорганизуйте код, а затем реорганизуйте тесты
Когда закончите, у вас должна быть полная, актуальная и READABLE документация вашего класса.
Концепция MakeSut
Тестовый код имеет те же требования к качеству, что и производственный код. MakeSut ()
- улучшает читаемость
- может быть легко реорганизован
- отлично поддерживает инъекцию зависимости.
Вот концепция:
[Test]
public void TestSomething()
{
var sut = MakeSut();
string result = sut.Do();
Assert.AreEqual("expected result", result);
}
Простейший MakeSut () просто возвращает проверенный класс:
private ClassUnderTest MakeSUT()
{
return new ClassUnderTest();
}
Когда нужны зависимости, их можно вводить здесь:
private ScriptHandler MakeSut(ICompiler compiler = null, ILogger logger = null, string scriptName="", string[] args = null)
{
//default dependencies can be created here
logger = logger ?? MockRepository.GenerateStub<ILogger>();
...
}
Можно сказать, что MakeSut - это просто альтернатива методам настройки и удаления, предоставляемая платформами Testrunner, и, возможно, усугубляет эти методы лучше для тестовой настройки и разгона.
Каждый может решить самостоятельно, какой способ использовать. Для меня MakeSut () обеспечивает лучшую читаемость и большую гибкость. И последнее, но не менее важное: концепция не зависит от какой-либо рамки testrunner.