Эквивалент NUnit для управления состоянием теста JUnit с помощью @Before/@After

Я пришел из мира Java и в основном использовал JUnit, и теперь у меня есть некоторые проблемы с выражением некоторых аспектов тестов с помощью NUnit 3. В JUnit каждый тест создает свой собственный экземпляр тестового класса, поэтому вполне допустимо создавать некоторые переменные экземпляра в тестовый класс, настройте их в @Before методе, тестовый метод и помощники могут свободно обращаться к этим переменным, не беспокоясь о том, что они будут перезаписаны другими тестами, запущенными параллельно, и @After аккуратно удаляет тестовые данные. С NUnit это не работает, и методы SetUp и TearDown в этом случае кажутся бесполезными, потому что экземпляр тестовой оснастки повторно используется между вызовами тестовых методов, поэтому поля класса тестовой оснастки могут (и перезаписываются) при каждом вызове тестовый метод (у моего класса есть несколько тестовых методов, и каждый из них генерирует несколько тестовых случаев, так что за один тестовый прогон приходится несколько десятков вызовов).

Я не знаю, как обойти эту проблему. В моем сценарии при настройке будет создана временная папка, которая будет использоваться в качестве рабочей папки для следующего тестового примера. После этого временная папка будет удалена, а все промежуточные файлы, созданные проверенным методом, будут удалены. Но теперь, когда SetUp создает и сохраняет временный путь к папке в поле экземпляра (чтобы он мог быть прочитан тестовой логикой и несколько сложными утверждениями и верификаторами), значение такого поля перезаписывается параллельными тестами. Я рассмотрел несколько подходов:

  • реализовать IDisposable, который будет представлять контекст каждого теста, и заключить его в using в каждом методе тестирования - мне не нравится эта идея, потому что мне не нравится идея использования IDisposable как чего-то другого, кроме инструмента управления ресурсами и объединения IDisposable с using, чтобы имитировать запахи установки/сноса для меня, как злоупотребление этой конкретной языковой особенностью,

  • создайте метод, который принимает делегат для фактической тестовой логики и вызывает пользовательские методы SetUpTestCase/TearDownTestCase. Метод вызовет установку, затем проверит делегат, а затем разрушит. Что мне не нравится в этом подходе, так это то, что он плохо сочетается с методами тестирования, которые принимают параметры - каждому набору методов тестирования, параметризованному определенным образом, потребуется соответствующий тип делегата. Также это как-то противоречит духу NUnit и способу описания тестовых методов атрибутами — в конце концов, зачем кому-то делегировать основную логику моего теста? Разве метод [Test] или [TestCase] не должен быть фактическим тестом?

  • возможно, есть какой-то способ использовать более продвинутые аспекты NUnit, такие как действия или некоторые обратные вызовы/триггеры/что-то еще, я просто слишком неопытен, чтобы увидеть это. Чего мне особенно не хватает, так это способа передачи данных из метода настройки (например, пути к созданной им временной папке) в следующий за ним тестовый метод. Я не могу использовать для этого поля экземпляра, и я не знаю, существует ли какая-либо структура «тегов», которая будет передавать специфичные для теста данные между методами, вызываемыми на разных этапах жизненного цикла теста?

Как правило, атрибуты SetUp и TearDown кажутся мне довольно бесполезными, если они не могут настроить тестовый пример без того, чтобы их результат был немедленно перезаписан другим параллельным тестовым набором. Что мне здесь не хватает?

Как я могу реализовать такой сценарий для каждого теста, поведение установки/снятия с ограниченной областью действия с помощью NUnit? Что я делаю не так, или что я упускаю?


person Maciek    schedule 21.12.2018    source источник


Ответы (1)


Как вы установили, экземпляр класса TestFixture создается один раз перед вызовом OneTimeSetUp; затем для каждого теста выполняется набор из SetUp, Test и TearDown; и, наконец, OneTimeTearDown.

Если вы хотите, чтобы тесты выполнялись параллельно (что не по умолчанию), вы должны указать Распараллеливаемый атрибут. Независимо от того, делаете вы это или нет, лучше, чтобы ваши тесты были написаны независимо друг от друга, чтобы они не конфликтовали друг с другом — они должны быть структурированы.

Шаблон AAA (Arrange, Act, Assert) — это распространенный способ структурирования модульных тестов для тестируемого метода. Если ваши тесты должны выполняться параллельно, то поля TestFixture не подходят для хранения информации, которая может конфликтовать между параллельными тестами, точно так же, как это не подходит для многопоточного класса.

Я бы предложил использовать частный метод в TestFixture для настройки временной папки - ему нужно будет каким-то образом указать уникальное имя папки, чтобы параллельные тесты не взаимодействовали - возможно, используйте Guid или CallerMemberName как часть имя папки и вернуть имя папки.

Этот метод следует вызывать из части теста Arrange. И вам нужно попробовать... наконец, обернуть остальную часть теста, чтобы убедиться, что папка будет удалена. Или вы могли бы пойти со своей идеей IDisposable - я не думаю, что в этом есть что-то неправильное: весь смысл в том, чтобы гарантировать очистку ресурсов (как управляемых, так и неуправляемых), когда что-то выходит за рамки.

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

person Richardissimo    schedule 22.12.2018