Best Practice: Initialize JUnit class fields in setUp() or at declaration?

前端 未结 9 1395
走了就别回头了
走了就别回头了 2020-12-07 09:20

Should I initialize class fields at declaration like this?

public class SomeTest extends TestCase
{
    private final List list = new ArrayList();

    publi         


        
相关标签:
9条回答
  • 2020-12-07 09:58

    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.

    0 讨论(0)
  • 2020-12-07 10:03

    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 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.

    0 讨论(0)
  • 2020-12-07 10:08

    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.

    0 讨论(0)
  • 2020-12-07 10:12

    If you're wondering specifically about the examples in the JUnit FAQ, such as the basic test template, I think the best practice being shown off there is that the class under test should be instantiated in your setUp method (or in a test method).

    When the JUnit examples create an ArrayList in the setUp method, they all go on to test the behavior of that ArrayList, with cases like testIndexOutOfBoundException, testEmptyCollection, and the like. The perspective there is of someone writing a class and making sure it works right.

    You should probably do the same when testing your own classes: create your object in setUp or in a test method, so that you'll be able to get reasonable output if you break it later.

    On the other hand, if you use a Java collection class (or other library class, for that matter) in your test code, it's probably not because you want to test it--it's just part of the test fixture. In this case, you can safely assume it works as intended, so initializing it in the declaration won't be a problem.

    For what it's worth, I work on a reasonably large, several-year-old, TDD-developed code base. We habitually initialize things in their declarations in test code, and in the year and a half that I've been on this project, it has never caused a problem. So there's at least some anecdotal evidence that it's a reasonable thing to do.

    0 讨论(0)
  • 2020-12-07 10:14

    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.

    0 讨论(0)
  • 2020-12-07 10:16

    I started digging myself and I found one potential advantage of using setUp(). If any exceptions are thrown during the execution of setUp(), JUnit will print a very helpful stack trace. On the other hand, if an exception is thrown during object construction, the error message simply says JUnit was unable to instantiate the test case and you don't see the line number where the failure occurred, probably because JUnit uses reflection to instantiate the test classes.

    None of this applies to the example of creating an empty collection, since that will never throw, but it is an advantage of the setUp() method.

    0 讨论(0)
提交回复
热议问题