Как сборщик "мусора" работает с модульными тестами?

Недавно, я спросил (и ответил), вопрос на StackOverflow о том, почему модульный тест будет работать, когда выполнено отдельно и затем перестать работать эпизодически, когда выполнено с целым пакетом модульных тестов. Посмотрите здесь: SQL Server и TransactionScope (с MSDTC): Эпизодически не может делать пересадку

Передача модульных тестов при выполнении по одному и затем сбое, когда выполнено вместе является классическим знаком, что что-то серьезно неправильно с кодом.

Я обнаружил, что существует определенная утечка ресурсов. Из-за тонкой ошибки, заставляя соединения с SQL-сервером, который не будет выпущен, у меня заканчивались соединения, и тесты перестали работать. AFAIK, это работает почти точно как утечка памяти; соединения выделяются от пула соединения и никогда не освобождаются так же, как память может быть выделена и затем не освобождена.

Однако это действительно оставляет меня с озадачивающим вопросом? Каково различие между запущением моих тестов по одному и выполнением их как комплект? Если тесты передают, когда выполнено по одному и затем перестали работать, когда выполнено вместе, то должна быть своего рода очистка, происходящая между тестовыми прогонами, который происходит только, когда тесты запущены по одному.

Я предугадываю, что это могло иметь некоторое отношение к тому, что сборщик "мусора" .NET делает или не делает между тестами. В одном случае соединения освобождены между тестами; в другом случае они не.

Как я могу объяснить это?

Обновление: К тем из Вас спрашивающий о специфических особенностях кода, это довольно просто. Я объявляю новое TransactionScope объект в моем методе Установки и располагает его в моем Teardown метод. Однако проблемный тест был управляемым данными тестом с 100 тестовыми сценариями; код под тестом заполнил a SqlDataReader объект от избранного оператора с помощью класса SqlHelper и затем не обращался к близкому методу SqlDataReader. Поскольку я использовал класс SqlHelper для получения SqlDataReader, я ожидал, что соединения были обработаны для меня. Не так!

Но разъясниться, я не спрашиваю о своей определенной ситуации. То, что я хочу знать: обычно, как ресурсы освобождены между тестами? Я предположил бы, что это будет некоторым приложением сборщика "мусора". Интересно, мог ли сборщик "мусора" все еще очищать предыдущий тест как следующие тестовые прогоны (состояние состязания?)

Обновление: Что я знаю о сборке "мусора" с Модульными тестами. После моего собственного любопытства я вытащил модульные тесты, которые перестали работать, потому что соединение оставили открытым SqlDataReader объект. Я пытался добавить System.GC.Collect() в конец каждого теста. Это успешно освободило соединения, но действительно налагает потерю производительности на ~50%.

6
задан Community 23 May 2017 в 12:03
поделиться

5 ответов

Сборка мусора - это периодическая фоновая задача. В частности, существует поток, который не делает ничего, кроме финализации объектов, которые уже были помечены как мертвые. Выполняя по одному тесту за раз, вы даете этому потоку возможность финализировать объекты, чтобы закрыть соединения.

2
ответ дан 16 December 2019 в 21:34
поделиться

Звучит реально, да. Для среды модульного тестирования не было бы ничего удивительного, если бы сборщик мусора запускался между тестами.

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

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

Сам сборщик мусора вряд ли будет вести себя иначе в модульные тесты, если только процесс запуска тестов не настроен определенным образом. С другой стороны, независимо от того, запускаете ли вы тесты в отладчике или нет , будет влиять на то, насколько рвется сборщик мусора и т. Д.

3
ответ дан 16 December 2019 в 21:34
поделиться

Обычно каждый тестовый запуск выполняется в отдельном домене приложения по нескольким причинам. Теперь, когда домен приложения выгружается, он освобождает связанные с ним ресурсы, так что открытые соединения закрываются и, следовательно, предотвращают проявление «утечки».

См. Также блог Cbrumme по этой теме .

2
ответ дан 16 December 2019 в 21:34
поделиться

Юнит-тесты проходят, если запускать их по одному по одному, но затем терпят неудачу при выполнении вместе - это классический признак того, что что-то серьезно не так с коде.

Я думаю, что что-то серьезно не так с тем, как вы написали свои модульные тесты. Каждый тест должен выполняться независимо от других тестов. Один из способов сделать это - обеспечить наличие методов setup и teardown ([SetUp][TearDown]), которые создают и очищают среду, необходимую для выполнения теста.

В методе Setup вы создаете свое соединение, а в методе teardown вы его утилизируете. Теперь перед запуском каждого теста будет вызываться ваш метод Setup, а после каждого теста будет вызываться ваш метод teardown, и это гарантирует, что вы не потеряете никаких ресурсов.

1
ответ дан 16 December 2019 в 21:34
поделиться

Ух ты, тут несколько проблем!

Во-первых, вы хотите, чтобы серия модульных тестов была БЫСТРОЙ. Не обращайтесь к базе данных, чтобы проверить бизнес-логику и т. Д.

Во-вторых, если в вашем производственном коде происходит утечка ресурсов (?), Это ваша основная проблема. Не решайте эту проблему, изменяя способ настройки / разборки тестового кода. Теперь, если ваш тестовый код выделяет системные ресурсы, но не удаляет их правильно, вам нужно исправить это правильно, а не пытаться контролировать, когда запускается сборщик мусора. Вам не следует об этом беспокоиться.

В-третьих, вам действительно не нужно создавать TransactionScope в ваших модульных тестах. Для меня это не имеет смысла. Что-то не так со стилем кодирования, который вы используете в своем тестовом коде.Модульные тесты - это не просто автоматизированные тесты, такие как интеграционные или системные тесты. Модульные тесты - это небольшие и целенаправленные тесты, которые проверяют поведение МАЛЕНЬКОЙ части производственного кода в ИЗОЛЯЦИИ, то есть независимо от всего остального производственного кода.

Теперь совет об утечке ресурсов. Хорошая практика программирования - использовать оператор using при создании одноразового объекта, чтобы гарантировать правильное размещение этих ресурсов.

using (SqlDataReader reader = ...)
{
   ...
}
0
ответ дан 16 December 2019 в 21:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: