모범 사례 : setUp () 또는 선언시 JUnit 클래스 필드를 초기화 하시겠습니까?
이런 선언에서 클래스 필드를 초기화해야합니까?
public class SomeTest extends TestCase
{
private final List list = new ArrayList();
public void testPopulateList()
{
// Add stuff to the list
// Assert the list contains what I expect
}
}
아니면 이런 식으로 setUp ()?
public class SomeTest extends TestCase
{
private List list;
@Override
protected void setUp() throws Exception
{
super.setUp();
this.list = new ArrayList();
}
public void testPopulateList()
{
// Add stuff to the list
// Assert the list contains what I expect
}
}
더 간결하고 첫 번째 필드를 사용할 수 있기 때문에 첫 번째 양식을 사용하는 경향이 있습니다. 설정 을 위해 setUp () 메소드를 사용할 필요 가없는 경우 에도 계속 사용해야합니까? 그 이유는 무엇입니까?
설명 : JUnit은 테스트 방법마다 테스트 클래스를 한 번 인스턴스화합니다. 그것은 list
내가 어디에 선언했는지에 관계없이 테스트 당 한 번 생성 된다는 것을 의미 합니다. 또한 테스트간에 시간적 종속성이 없음을 의미합니다. 따라서 setUp ()을 사용하면 이점이없는 것 같습니다. 그러나 JUnit FAQ에는 setUp ()에서 빈 컬렉션을 초기화하는 많은 예제가 있으므로 이유가 있어야한다고 생각합니다.
기본 테스트 템플릿 과 같은 JUnit FAQ의 예제에 대해 특별히 궁금한 점이 있다면 테스트 중인 클래스가 setUp 메소드 또는 테스트 메소드에서 인스턴스화되어야한다는 것이 모범 사례라고 생각합니다. .
JUnit 예제가 setUp 메소드에서 ArrayList를 작성하면 testIndexOutOfBoundException, testEmptyCollection 등과 같은 경우를 포함하여 해당 ArrayList의 동작을 테스트합니다. 누군가 클래스를 작성하고 올바르게 작동하는지 확인하는 관점이 있습니다.
자신의 클래스를 테스트 할 때도 똑같이해야합니다. setUp 또는 테스트 방법으로 객체를 생성하여 나중에 중단하면 합리적인 결과를 얻을 수 있습니다.
반면에 테스트 코드에서 Java 콜렉션 클래스 (또는 그 문제에 대한 다른 라이브러리 클래스)를 사용하는 경우 테스트하려는 것이 아니라 테스트 픽스처의 일부일 수 있습니다. 이 경우 의도 한대로 작동한다고 가정 할 수 있으므로 선언에서 초기화해도 문제가되지 않습니다.
가치있는 것을 위해, 나는 몇 년 전 TDD가 개발 한 상당히 큰 코드 기반으로 작업합니다. 우리는 습관적으로 테스트 코드에서 선언으로 물건을 초기화하며,이 프로젝트에 참여한 1 년 반 동안 문제가 발생하지 않았습니다. 따라서 합리적으로해야한다는 일화적인 증거가 적어도 있습니다.
나는 내 자신을 파기 시작했고을 사용하는 것의 한 가지 잠재적 이점을 발견했습니다 setUp()
. 을 실행하는 동안 예외가 발생 setUp()
하면 JUnit은 매우 유용한 스택 추적을 인쇄합니다. 반면에 객체 생성 중에 예외가 발생하면 JUnit이 테스트 사례를 인스턴스화 할 수없고 오류가 발생한 행 번호를 볼 수 없다는 오류 메시지가 표시됩니다. 아마도 JUnit은 리플렉션을 사용하여 테스트를 인스턴스화했기 때문일 것입니다 클래스.
빈 컬렉션을 생성하는 예에는 해당되지 않습니다. 빈 컬렉션은 결코 발생하지 않기 때문에이 setUp()
방법이 유리합니다 .
Alex B의 답변 외에도.
It is even required to use the setUp method to instantiate resources in a certain state. Doing this in the constructor is not only a matter of timings, but because of the way JUnit runs the tests, each test state would be erased after running one.
JUnit first creates instances of the testClass for each test method and starts running the tests after each instance is created. Before running the test method, its setup method is ran, in which some state can be prepared.
If the database state would be created in the constructor, all instances would instantiate the db state right after each other, before running each tests. As of the second test, tests would run with a dirty state.
JUnits lifecycle:
- Create a different testclass instance for each test method
- Repeat for each testclass instance: call setup + call the testmethod
With some loggings in a test with two test methods you get: (number is the hashcode)
- Creating new instance: 5718203
- Creating new instance: 5947506
- Setup: 5718203
- TestOne: 5718203
- Setup: 5947506
- TestTwo: 5947506
In JUnit 4:
- For the Class Under Test, initialize in a
@Before
method, to catch failures. - For other classes, initialize in the declaration...
- ...for brevity, and to mark fields
final
, exactly as stated in the question, - ...unless it is complex initialization that could fail, in which case use
@Before
, to catch failures.
- ...for brevity, and to mark fields
- For global state (esp. slow initialization, like a database), use
@BeforeClass
, but be careful of dependencies between tests. - Initialization of an object used in a single test should of course be done in the test method itself.
Initializing in a @Before
method or test method allows you to get better error reporting on failures. This is especially useful for instantiating the Class Under Test (which you might break), but is also useful for calling external systems, like filesystem access ("file not found") or connecting to a database ("connection refused").
It is acceptable to have a simple standard and always use @Before
(clear errors but verbose) or always initialize in declaration (concise but gives confusing errors), since complex coding rules are hard to follow, and this isn't a big deal.
Initializing in setUp
is a relic of JUnit 3, where all test instances were initialized eagerly, which causes problems (speed, memory, resource exhaustion) if you do expensive initialization. Thus best practice was to do expensive initialization in setUp
, which was only run when the test was executed. This no longer applies, so it is much less necessary to use setUp
.
This summarizes several other replies that bury the lede, notably by Craig P. Motlin (question itself and self-answer), Moss Collum (class under test), and dsaff.
In JUnit 3, your field initializers will be run once per test method before any tests are run. As long as your field values are small in memory, take little set up time, and do not affect global state, using field initializers is technically fine. However, if those do not hold, you may end up consuming a lot of memory or time setting up your fields before the first test is run, and possibly even running out of memory. For this reason, many developers always set field values in the setUp() method, where it's always safe, even when it's not strictly necessary.
Note that in JUnit 4, test object initialization happens right before test running, and so using field initializers is safer, and recommended style.
In your case (creating a list) there is no difference in practice. But generally it is better to use setUp(), because that will help Junit to report Exceptions correctly. If an exception occurs in constructor/initializer of a Test, that is a test failure. However, if an exception occurs during setup, it is natural to think of it as some issue in setting up the test, and junit reports it appropriately.
I prefer readability first which most often does not use the setup method. I make an exception when a basic setup operation takes a long time and is repeated within each test.
At that point I move that functionality into a setup method using the @BeforeClass
annotation (optimize later).
Example of optimization using the @BeforeClass
setup method: I use dbunit for some database functional tests. The setup method is responsible for putting the database in a known state (very slow... 30 seconds - 2 minutes depending on amount of data). I load this data in the setup method annotated with @BeforeClass
and then run 10-20 tests against the same set of data as opposed to re-loading/initializing the database inside each test.
Using Junit 3.8 (extending TestCase as shown in your example) requires writing a little more code than just adding an annotation, but the "run once before class setup" is still possible.
Since each test is executed independently, with a fresh instance of the object, there's not much point to the Test object having any internal state except that shared between setUp()
and an individual test and tearDown()
. This is one reason (in addition to the reasons others gave) that it's good to use the setUp()
method.
Note: It's a bad idea for a JUnit test object to maintain static state! If you make use of static variable in your tests for anything other than tracking or diagnostic purposes, you are invalidating part of the purpose of JUnit, which is that the tests can (an may) be run in any order, each test running with a fresh, clean state.
The advantages to using setUp()
is that you don't have to cut-and-paste initialization code in every test method and that you don't have test setup code in the constructor. In your case, there is little difference. Just creating an empty list can be done safely as you show it or in the constructor as it's a trivial initialization. However, as you and others have pointed out, anything that can possibly throw an Exception
should be done in setUp()
so you get the diagnostic stack dump if it fails.
In your case, where you are just creating an empty list, I would do the same way you are suggesting: Assign the new list at the point of declaration. Especially because this way you have the option of marking it final
if this makes sense for your test class.
The constant values (uses in fixtures or assertions) should be initialized in their declarations and
final
(as never change)the object under test should be initialized in the setup method because we may set things on. Of course we may not set something now but we could set it later. Instantiating in the init method would ease the changes.
dependencies of the object under test if these are mocked, should not even be instantiated by yourself : today the mock frameworks can instantiate it by reflection.
A test without dependency to mock could look like :
public class SomeTest {
Some some; //instance under test
static final String GENERIC_ID = "123";
static final String PREFIX_URL_WS = "http://foo.com/ws";
@Before
public void beforeEach() {
some = new Some(new Foo(), new Bar());
}
@Test
public void populateList()
...
}
}
A test with dependencies to isolate could look like :
@RunWith(org.mockito.runners.MockitoJUnitRunner.class)
public class SomeTest {
Some some; //instance under test
static final String GENERIC_ID = "123";
static final String PREFIX_URL_WS = "http://foo.com/ws";
@Mock
Foo fooMock;
@Mock
Bar barMock;
@Before
public void beforeEach() {
some = new Some(fooMock, barMock);
}
@Test
public void populateList()
...
}
}
'Programing' 카테고리의 다른 글
빈 객체를 반환해야 함 (0) | 2020.07.29 |
---|---|
Python 3으로 Python 2 객체 언 클링 (0) | 2020.07.29 |
레이크의 '환경'과제는 무엇입니까? (0) | 2020.07.29 |
Eclipse와 같이 IntelliJ IDEA의 클래스를 자동으로 가져 오는 방법 (또는 바로 가기)은 무엇입니까? (0) | 2020.07.29 |
System.gc ()는 언제 작동합니까? (0) | 2020.07.29 |