JUnit: using constructor instead of @Before
I'm using JUnit 4. I can't see the difference between initializing in the constructor or using a dedicated init function annotated by @Before
. Does this mean that 开发者_运维百科I don't have to worry about it?
Is there any case when @Before
gives more than just initializing in the constructor?
No, using the constructor to initialize your JUnit test fixture is technically equal to using the @Before
method (due to the fact that JUnit creates a new instance of the testing class for each @Test
). The only (connotational) difference is that it breaks the symmetry between @Before
and @After
, which may be confusing for some. IMHO it is better to adhere to conventions (which is using @Before
).
Note also that prior to JUnit 4 and annotations, there were dedicated setUp()
and tearDown()
methods - the @Before
and @After
annotations replace these, but preserve the underlying logic. So using the annotations also makes life easier for someone migrating from JUnit 3 or earlier versions.
Notable differences
More details from comments:
@Before
allows overriding parent class behavior, constructors force you to call parent class constructors- The constructor runs before subclass constructors and
@Rule
methods,@Before
runs after all of those - Exceptions during
@Before
cause@After
methods to be called, Exceptions in constructor don't
@Before makes more sense to use in certain cases because it gets called AFTER the constructor for the class. This difference is important when you're using a mock framework like Mockito with @Mock annotations, because your @Before method will be called after the mocks are initialized. Then you can use your mocks to provide constructor arguments to the class under test.
I find this to be a very common pattern in my unit tests when using collaborating beans.
Here's an (admittedly contrived) example:
@RunWith(MockitoJUnitRunner.class)
public class CalculatorTest {
@Mock Adder adder;
@Mock Subtractor subtractor;
@Mock Divider divider;
@Mock Multiplier multiplier;
Calculator calculator;
@Before
public void setUp() {
calculator = new Calculator(adder,subtractor,divider,multiplier);
}
@Test
public void testAdd() {
BigDecimal value = calculator.add(2,2);
verify(adder).add(eq(2),eq(2));
}
}
I prefer to use constructors to initialize my test objects because it allows me to make all the members final
so that the IDE or compiler will tell me when the constructor forgot to initialize a member and prevent another method from setting them.
IMHO, @Before
violates one of the most important Java conventions, that of relying on the constructor to completely initalize objects!
JUnit 5 also has better support for constructor injection.
I prefer to declare my fixtures as final and initialize them inline or in the constructor so I don't forget to initialize them! However, since exceptions thrown in @Before are handled in a more user-friendly way, I usually initialize the object under test in @Before.
Quote from http://etutorials.org/Programming/Java+extreme+programming/Chapter+4.+JUnit/4.6+Set+Up+and+Tear+Down/
You may be wondering why you should write a setUp( ) method instead of simply initializing fields in a test case's constructor. After all, since a new instance of the test case is created for each of its test methods, the constructor is always called before setUp( ). In a vast majority of cases, you can use the constructor instead of setUp( ) without any side effects.
In cases where your test case is part of a deeper inheritance hierarchy, you may wish to postpone object initialization until instances of derived classes are fully constructed. This is a good technical reason why you might want to use setUp( ) instead of a constructor for initialization. Using setUp( ) and tearDown( ) is also good for documentation purposes, simply because it may make the code easier to read.
There is one thing that constructor can archive but not @Before.
You have to use a constructor when you need to initial fields defined in parent class. For example:
abstract class AbstractIT {
int fieldAssignedInSubClass;
public AbstractIT(int fieldAssignedInSubClass) {
this.fieldAssignedInSubClass= fieldAssignedInSubClass;
}
@Before
void before() {
// comsume fieldAssignedInSubClass
}
}
public class ChildIT extends AbstractIT{
public ChildIT() {
// assign fieldAssignedInSubClass by constructor
super(5566);
}
@Before
void before() {
// you cannot assign fieldAssignedInSubClass by a @Before method
}
}
@Before
does make sense to use for several reasons. It makes your test code more readable. It matches @After
annotation which is responsible for releasing used resources and is a counterpart of the @BeforeClass
annotation.
There is no difference except that the constructor is the only method ehere can initialize the @Rule objects:
public class TestClass {
@Rule
public SomeRule rule;
public TestClass() {
// code to initialize the rule field
conf = new RuleConf()
rule = new SomeRule(conf)
}
}
精彩评论