Действительно ли необходимо уничтожить объекты в методах JUnit teardown?
Я был заинтригован ответом на аналогичный вопрос. Я считаю, что это неверно. Поэтому я создал тестовый код. Мой вопрос заключается в том, что этот код доказывает/опровергает/отрицает гипотезу о том, что полезно аннулировать переменные-члены в методах слежения? Я тестировал его с помощью JUnit4.8.1.
JUnit создает новый экземпляр тестового класса для каждого из 4 тестов. Каждый экземпляр содержит Object obj. Этот obj также вставлен как ключ статического WeakHashMap. Если и когда JUnit выпускает свои ссылки на тестовый экземпляр, связанное с ним значение obj будет слабо ссылаться и, следовательно, иметь право на gc. Тест пытается заставить gc. Размер WeakHashMap скажет мне, являются ли objs gc'ed. Некоторые тесты аннулировали переменную obj, а другие - нет.
import org . junit . Before ;
import org . junit . After ;
import org . junit . Test ;
import java . util . ArrayList ;
import java . util . WeakHashMap ;
import java . util . concurrent . atomic . AtomicInteger ;
import static org . junit . Assert . * ;
public class Memory
{
static AtomicInteger idx = new AtomicInteger ( 0 ) ;
static WeakHashMap < Object , Object > map = new WeakHashMap < Object , Object > ( ) ;
int id ;
Object obj ;
boolean nullify ;
public Memory ( )
{
super ( ) ;
}
@ Before
public void before ( )
{
id = idx . getAndIncrement ( ) ;
obj = new Object ( ) ;
map . put ( obj , new Object ( ) ) ;
System . out . println ( "<BEFORE TEST " + id + ">" ) ;
}
void test ( boolean n )
{
nullify = n ;
int before = map . size ( ) ;
gc ( ) ;
int after = map . size ( ) ;
System . out . println ( "BEFORE=" + before + "\tAFTER=" + after ) ;
}
@ Test
public void test0 ( )
{
test ( true ) ;
}
@ Test
public void test1 ( )
{
test ( false ) ;
}
@ Test
public void test2 ( )
{
test ( true ) ;
}
@ Test
public void test3 ( )
{
test ( false ) ;
}
@ After
public void after ( )
{
if ( nullify )
{
System . out . println ( "Nullifying obj" ) ;
obj = null ;
}
System . out . println ( "<AFTER TEST " + id + ">" ) ;
}
/**
* Try to force a gc when one is not really needed.
**/
void gc ( )
{
ArrayList < Object > waste = new ArrayList < Object > ( ) ;
System . gc ( ) ; // only a suggestion but I'll try to force it
list :
while ( true ) // try to force a gc
{
try
{
waste . add ( new Object ( ) ) ;
}
catch ( OutOfMemoryError cause )
{
// gc forced? should have been
waste = null ;
break list ;
}
}
System . gc ( ) ; // only a suggestion but I tried to force it
}
}
Я запустил код с помощью интерфейса командной строки (используя опцию -Xmx128k для увеличения сбора мусора) и получил следующий результат
.<BEFORE TEST 0>
BEFORE=1 AFTER=1
Nullifying obj
<AFTER TEST 0>
.<BEFORE TEST 1>
BEFORE=2 AFTER=1
<AFTER TEST 1>
.<BEFORE TEST 2>
BEFORE=2 AFTER=1
Nullifying obj
<AFTER TEST 2>
.<BEFORE TEST 3>
BEFORE=2 AFTER=1
<AFTER TEST 3>
Test0 obj был аннулирован, а в Test1 он gc'ed. Но test1 obj не был аннулирован, и он получил gc'ed в Test2. Это говорит о том, что обнуление объектов не требуется.
Ответы
Ответ 1
Тесты стиля JUnit 4.x и комплекты тестов обрабатывают это иначе, чем тестовые комплекты JUnit 3.x.
Короче говоря, вы должны устанавливать поля в null в тестах стиля JUnit3, но вам не нужно в тестах стиля JUnit4.
С помощью тестов стиля JUnit 3.x TestSuite
содержит ссылки на другие объекты Test
(которые могут быть объектами TestCase
или другими объектами TestSuite
). Если вы создадите пакет с большим количеством тестов, тогда будут жесткие ссылки на все объекты leaf TestCase
для всего прогона самого удаленного набора. Если некоторые из объектов TestCase выделяют объекты в setUp()
, которые занимают много памяти, а ссылки на эти объекты хранятся в полях, которые не установлены на null
в tearDown()
, тогда у вас может быть проблема с памятью.
Другими словами, для тестов стиля JUnit 3.x спецификация тех тестов для запуска ссылок на фактические объекты TestCase
. Любые объекты, доступные из объекта TestCase
, будут храниться в памяти во время тестового прогона.
Для тестов стиля JUnit 4.x в спецификации тех тестов для запуска используются Description объекты. Объект Description
- это объект значения, который указывает, что нужно запускать, но не как его запустить. Тестирование выполняется объектом Runner
, который принимает Description
теста или набора и определяет, как выполнить тест. Даже уведомление о статусе теста для тестового слушателя использует объекты Description
.
По умолчанию для тестовых примеров JUnit4 по умолчанию JUnit4 хранится ссылка на тестовый объект только на время выполнения этот тест. Если вы используете пользовательский бегун (через аннотацию @RunWith
), этот бегун может или не может ссылаться на тесты в течение более длительных периодов времени.
Возможно, вам интересно, что произойдет, если вы включите тестовый класс в стиле JUnit3 в стиле Suite в стиле JUnit4? JUnit4 вызовет new TestSuite(Class)
, который создаст отдельный экземпляр TestCase
для каждого тестового метода. Бегун будет хранить ссылку на TestSuite
на весь период тестового прогона.
Короче говоря, если вы пишете тесты в стиле JUnit4, не беспокойтесь о том, чтобы установить поля тестового случая на null
в результате срыва (конечно, свободные ресурсы). Если вы пишете тесты типа JUnit3, которые выделяют большие объекты в setUp()
и сохраняют эти объекты в полях TestCase
, рассмотрите установку полей null
.
Ответ 2
Это действительно не нужно, но это помогает сборщику мусора, когда ему нужно знать, какие переменные используются или нет; null переменная в значительной степени гарантирована, чтобы быть хорошим кандидатом на сбор мусора.
Ответ 3
Нет, это не обязательно.
Методы сбрасывания предназначены для объектов с циклическим циклом жизни, которые любят быть явно закрытыми, завершенными, отключенными, удаленными, отключенными, незарегистрированными или другими.
Даже если ваши ссылки перейдут к следующему тестовому примеру, они будут перезаписаны вашим методом настройки и не будут заменены и, следовательно, будут иметь право на сбор мусора.
И событие, если JUnit создает новый экземпляр вашего тестового примера для каждого метода (что, кажется, так), те тестовые объекты не удерживаются. По крайней мере, если тест проходит, в соответствии с быстрым экспериментом. Таким образом, многое из этого будет собрано в досуге в любом случае.