JUnit: using constructor instead of @Before
Asked Answered
G

8

103

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?

Gunthar answered 23/5, 2011 at 7:22 Comment(3)
possible duplicate of setUp/tearDown (@Before/@After) why we need them in JUnit?Housewife
See also: #512684Begrime
Are you sure you mean @Before, not @BeforeClass? Check the difference here.Pulsatile
D
112

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
Delicacy answered 23/5, 2011 at 7:30 Comment(10)
@Before is not called multiple times ? (before each test method ?)Tsui
@Sylvain, as JUnit creates a new test class instance for each test method, the constructor and @Before is called exactly the same amount of times.Ebsen
In the meantime I've read an important difference: if an exception is thrown in the constructor then there will be no object to run @After on. So I've kept using the annotations and only keep the minimal constructor.Gunthar
One difference is that code in the constructor will be executed before any Rules are applied. If you have Rules that do something before running the test, that code is executed before any @Before methodsWorse
@PéterTörök, I agree that it is better to adhere to conventions, which is why I prefer to use constructors to initialize my test objects instead of @Before methods. IMHO, @Before violates one of the most important Java conventions, that of relying on the constructor to initalize objects! (Not to mention, I can make my class members final and the IDE or compiler will tell me when I forgot to initialize a member.)Freebooter
@vbence, won't the test fail if the constructor or an @After method fails?Freebooter
@DerekMahar I'm sure it will (if exception is not the goal of the test). But you might want to clean up after setting up the test if external state (filesystem/database) is involved. - Those are not typical unit tests though.Gunthar
@DerekMahar, there are always different circles of convention in play, and IMHO it is usually recommended to adhere to the most specific of these, which in this case is JUnit. YMMV. Note that the point of the Java (or rather, general OO) convention of using constructors is to ensure that objects are always properly initialized before usage. In JUnit, these specific objects represent test cases, and are initialized and used only by the framework itself, in a very specific and strict way which guarantees proper setup and teardown.Ebsen
@DerekMahar, not to mention that not all xUnit clones behave the same way in this respect. E.g. NUnit does not create a new instance of the test class for each test method execution, so using the constructor instead of the [SetUp] method (the NUnit equivalent of @Before) would actually be an error there. To me this is analogous to the "rely on interfaces, not implementations" maxime, making one's code more portable and easier to maintain in the long run.Ebsen
@PéterTörök, I did not know that NUnit did not follow this convention. I think you've convinced me that using @Before is a good idea, though it bothers me that when using @Before, I can't make my data member references final.Freebooter
K
32

@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));
    }
}
Knorring answered 10/9, 2012 at 19:24 Comment(2)
Why can't you do the same thing in a constructor? In a constructor, just initialize the calculator with all the mocks which the test runner should have initialized first.Freebooter
Also, you defined those Mocks to be @Nonnull. You need a constructor to fix that. A setUp() method won’t to, because you can instantiate the CalculatorTest-class in another test, which will then cause NPEs.Spagyric
F
14

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.

Freebooter answered 2/10, 2014 at 20:49 Comment(0)
F
5

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.

Farwell answered 11/8, 2011 at 12:1 Comment(0)
B
4

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.

Bohemia answered 14/1, 2019 at 14:56 Comment(0)
G
1

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
   } 
}
Gulick answered 27/12, 2017 at 7:39 Comment(0)
M
0

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

Markus answered 23/5, 2011 at 7:30 Comment(0)
L
0

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)
    }
}
Lemuel answered 20/11, 2016 at 8:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.