views:

228

answers:

6

Hi All,

I believe that we are all know that setUp (@Before) will execute before any test method and tearDown(@After) will execute after test method.

Also we know that Junit will create one instance of Test per test method.

my question is that can we just move setUp method content to class Constructor and remove setUp method? is there any specific reason to keep setUp method?

+2  A: 

The reason you need this is that for many tests you often need to initialize state before each test so that the tests can all make assumptions about the start state they're running in.

Suppose your test class wraps, say database access. After each test you'd want to remove whatever changes your tests have made to the db - if you didn't do that, each test runs against a slightly modified database. Additionally, any given test may see a different set of changes if some subset of the previous tests have failed. For example, suppose test1 does an insert, test2 checks that you're accurately reading the table size. Day 1, test1 fails, and 0 is correct. Day 2, test1 succeeds, and 1 is correct?

BTW, junit also supports @BeforeClass if you want to do a global setup, and setup and teardowns are optional.

Steve B.
I don't think the op is disputing the fact that setup code is useful. Rather, the question is why don't we just use the class constructor to do what `setUp` does question.
Nader Shirazie
+8  A: 

This (old) JUnit best practices article puts it like this:

Do not use the test-case constructor to set up a test case

Setting up a test case in the constructor is not a good idea. Consider:

public class SomeTest extends TestCase
   public SomeTest (String testName) {
      super (testName);
      // Perform test set-up
   }
}

Imagine that while performing the setup, the setup code throws an IllegalStateException. In response, JUnit would throw an AssertionFailedError, indicating that the test case could not be instantiated. Here is an example of the resulting stack trace:

junit.framework.AssertionFailedError: Cannot instantiate test case: test1   
    at junit.framework.Assert.fail(Assert.java:143)
    at junit.framework.TestSuite.runTest(TestSuite.java:178)
    at junit.framework.TestCase.runBare(TestCase.java:129)
    at junit.framework.TestResult.protect(TestResult.java:100)
    at junit.framework.TestResult.runProtected(TestResult.java:117)
    at junit.framework.TestResult.run(TestResult.java:103)
    at junit.framework.TestCase.run(TestCase.java:120)
    at junit.framework.TestSuite.run(TestSuite.java, Compiled Code)
    at junit.ui.TestRunner2.run(TestRunner.java:429)

This stack trace proves rather uninformative; it only indicates that the test case could not be instantiated. It doesn't detail the original error's location or place of origin. This lack of information makes it hard to deduce the exception's underlying cause.

Instead of setting up the data in the constructor, perform test setup by overriding setUp(). Any exception thrown within setUp() is reported correctly. Compare this stack trace with the previous example:

java.lang.IllegalStateException: Oops
    at bp.DTC.setUp(DTC.java:34) 
    at junit.framework.TestCase.runBare(TestCase.java:127)
    at junit.framework.TestResult.protect(TestResult.java:100)
    at junit.framework.TestResult.runProtected(TestResult.java:117)
    at junit.framework.TestResult.run(TestResult.java:103)
    ...

This stack trace is much more informative; it shows which exception was thrown (IllegalStateException) and from where. That makes it far easier to explain the test setup's failure.

Pascal Thivent
Dont' you hate it when you're typing up an answer and someone (especially with higher rep than you) beats you to the punch with the same answer! I do! :-) I was formatting the exact same quote from that same article in my answer when I saw a new answer was posted and it was the same as mine. Must type faster ...
Bert F
@Bert I think we all do. And I'm sorry it happened to you this time.
Pascal Thivent
@Pascal - Thanks :-) Just had to vent.
Bert F
+1  A: 

A custom runner such as SpringJUnit4ClassRunner may need to run some codes between the constructor and @Before method. In this case, the runner may inject some dependency which the @Before methods needs. But dependency injection can only be run after the object is constructed.

RichN
+2  A: 
Bert F
+2  A: 

At work we've discovered something rather interesting which answers your question. When you run a test suite, especially a large set of tests (200+) JUnit starts to use a LOT of memory, this is because ALL the tests are instanced before any actual test method is run.

We ran into a "memory leak" because of this because we used Spring to wire in some JPA EntiryManager objects for our database tests, this became A LOT of objects and a lot of memory and about half way through the tests we were getting OutOfMemory exceptions.

IMHO, best practise is to use setUp and tearDown to inject your dependencies and null out any and all class references, this will make your tests run faster and save you a lot of head ache!

Hope you learn from our mistakes :)

BjornS
good point. thanks for sharing.
mohammad shamsi
A: 

I think some reason should like the following:

1).If you move @Before contents to Constructor, That's fine, but the @After contents where you get to move? 2).the differences of Constructor and @Before/@After is that Constructor should be used to instance some for class, @Before/@After is for preparing test case resources. you may take a look about:

http://www.javarmi.com/2010/08/junit-tutorial-%E2%80%93-basic-usage-and-annotation/

GoingWell