Pull to refresh

Comments 3

Без обид.

Но для меня важный критерий для тестов - может ли посторонний человек посмотреть на код теста, на результат теста и через 10-30 секунд сказать что тест проверяет, и почему он сломался. Пусть даже с не 100% точностью.

Вот смотрю я на код тестов, и понимаю, что даже примерно ответить на такой вопрос сложно. Без погружения к предметную область данного конкретного теста.

Тесты, о которых Вы пишете - то, к чему надо стремиться. В рафинированном виде такие встречаются только в обучающих материалах по TDD. В жизни обычно приходишь на новый проект, смотришь на юнит тесты и ужасаешься. В некоторых компаниях их до сих пор вообще не применяют. А даже там, где юнит тестирование организованно более или менее прилично, могут быть иерархии наследования тестовых классов с переопределениями вспомогательных методов типа SetUp(), TearDown() или CreateSut(), чтобы не было дублирующегося кода. Если впадать в крайности пуризма, то такие методы даже без наследования тестовых классов надо объявлять ересью.

может ли посторонний человек посмотреть на код теста...

Это решается конвенционально. Также как, например, организации тестов по принципу AAA или создание sut только через CreateSut(). Просто вся команда знает (и в проекте даже соответствующая инструкция лежит), что если тест помечен DeclarativeCaseAttribute<TValidator, TScript, TCheck>, то для

[TestFixture]
public class DemoTests : IDeclarativeTest
{
    public void Test<TValidator, TScript, TCheck>(bool expected)
        where TValidator : IValidator, new()
        where TScript : IScript, new()
        where TCheck : ICheck, new() =>
        DefaultDeclarativeTest.Test<TValidator, TScript, TCheck>(expected);

    [DeclarativeCase<OrdinaryValidator, AdvancedAttack, AttackDetected>(false)]
    [DeclarativeCase<AdvancedValidator, AdvancedAttack, AttackDetected>(true)]
    public void TestAdvancedAttack() { }
}

это означает, что фактически тест содержится в void Test<TValidator, TScript, TCheck>(bool) тестового класса, а для

[TestFixture, Declarative]
public class DemoTests
{
    [DeclarativeCase<OrdinaryValidator, AdvancedAttack, AttackDetected>(false)]
    [DeclarativeCase<AdvancedValidator, AdvancedAttack, AttackDetected>(true)] 
    public void TestAdvancedAttack() { }
}

подразумевается, что непосредственно код теста лежит в void Test() аттрибута (пара кликов/хот-кеев - и перед Вами фактическая реализация теста). Если так делать постоянно, то оно начнет восприниматься, как само собой разумеющееся. Многие ли задумываются о том, что происходит под капотом, когда для добавления теста просто пишут [Test], а потом в Test Explorer видят результат его выполнения?

Вообще статья является продолжением другой публикации с пометкой "ненормальное программирование". Я необоснованно предположил, что в сознании читающего этот аттрибут неявно наследуется. Сейчас исправлю. В обеих описывается не как нужно делать, а как можно. Вот пришла человеку в голову занимательная идея, он ее воплотить попытался, глубже в инструменты погрузился, столкнулся с ограничениями, но нашел workaround'ы, а свой опыт здесь описал. Наверное, с названием "Как хакнуть NUnit, чтобы заставить делать странное" воспринималось бы иначе.

Тесты, о которых Вы пишете - то, к чему надо стремиться. В рафинированном виде такие встречаются только в обучающих материалах по TDD. В жизни обычно приходишь на новый проект, смотришь на юнит тесты и ужасаешься. В некоторых компаниях их до сих пор вообще не применяют.

Это все так.

И эксперименты проводить полезно.

Но такое ощущение, что люди забывают, что требования к рабочему коду и коду тестов - разные. Все то, что хорошо для рабочего кода, далеко не всегда хорошо для тестового кода. И наоборот.

Sign up to leave a comment.

Articles