4

@Before notation in JUnit Testing is needed because several tests need similar objects created before they can run.

But I don't get the difference between instantiating an object before the testcase function as a global object and putting inside a @Before.

For example, I am testing my chess program and I am testing if my Piece object moves to a correct place by doing :

public class PawnTest { //The Test Class itself

Board board = new Board();

@Test
/**
 * Testing the right movement
 */
public void correctMovementTest() {
    Pawn p1 = new Pawn(Player.UP);
    board.placePiece(4, 3, p1);
    board.movePieceTo(5, 3, p1);
    assertEquals(board.getPiece(5, 3), p1);
}

@Test
/**
 * Testing the right movement
 */
public void correctMovementTest2() {
    Pawn p1 = new Pawn(Player.UP);
    board.placePiece(4, 3, p1);
    board.movePieceTo(6, 3, p1);
    assertEquals(board.getPiece(6, 3), p1);
}

....

So wouldn't it just work if I decalre Board and Pawn p1 outside the testcase method? Why would we need @Before in the test class?

Also, doing this won't work

@Before
public void setup() {
    Board board = new Board();
    Pawn p1 = new Pawn(Player.UP);
}

I thought this would actually set up the objects before the test cases so that I won't have to set them up on every test cases, but the test cases wouldn't actually read the p1 object and the board.

Sabir Khan
  • 9,826
  • 7
  • 45
  • 98
user6792790
  • 668
  • 1
  • 7
  • 22

4 Answers4

10

@Before annotation is used to do something before executing each test case in your class. So, basically you're on the right way. To make your code work, you need to declare your Board and Pawn outside of function scope.

Board board = null;
Pawn p1 = null;

@Before
public void setup() {
    board = new Board();
    p1 = new Pawn(Player.UP);
}

There is also @BeforeClass annotation available to execute some actions once before executing entire test suite - for example start embedded database. Hope it helps!

Sergey Prokofiev
  • 1,815
  • 1
  • 12
  • 21
1

@Before and @BeforeClass are useful to set up peripherals used by your tests. @Before will run before every test, while @BeforeClass will run before the test suite (eg once before all the tests.)

Lets say your tests all have some common setup that needs to be done before running. You could always just create some sort of utility method setup and then call it manually before every class:

private void setup() {
  // do stuff
}

@Test
public void testFoo() {
  setup();
  // do test
}

@Test
public void testBar() {
  setup();
  // do test
}

... Or you could just annotate the setup method with @Before and have JUnit do that for you:

@Before // JUnit will now call before every test, without you needing to do anything
public void setup() {
  // do stuff
}

@Test
public void testFoo() {
  // do test
}

@Test
public void testBar() {
  // do test
}

The @BeforeClass annotation works under a similar concept, except that it hooks before the entire suite and runs once.

Personally I find myself using these situations when I have to mock out dependencies.

Consider, for example, a class which has a dependency on a database connection. In my @Before, I'll create a mocked instance of this database connection that my tests can inject/use as needed:

private DbConnector mockDb;

@Before
public void initMocks() {
  mockDb = Mockito.mock(DbConnector.class);
}

If I don't mind my tests all sharing the same instance, I can make mockDb static and use @BeforeClass.

In your particular case, you have Board instance. If you want your tests to share the same Board -- eg there are no side effects to running different tests on a given board -- then you can create the new instance using @BeforeClass:

private static Board board;

@BeforeClass
public static void initBoard() {
  board = new Board();
  // other board init logic here
}

Maybe we want to share the same Board instance everywhere, but we need to do some cleanup or setup logic. Then we can annotate such methods with @Before and @After to run them around each test instance to put the board into the expected state.

@Before
public void setupBoard() {
  // setup logic, runs before each test (maybe puts the pieces where they should go for a new game?)
}

@After
public void resetBoard() {
  // reset logic, runs after every test (maybe remove scores, resets mocks, etc?)
}

When to use these annotations, and which specific ones to use really rely on how your tests are designed. If, for example, setting up your Board is simply just new Board(); and nothing else, you don't really need the @BeforeClass method and you can just use a variable.

Roddy of the Frozen Peas
  • 14,380
  • 9
  • 49
  • 99
1

Technically , having instance fields ( declaration plus initialization both ) in your test class and having instances created in @Before method ( only references declared as instance fields ) are same thing i.e. your global data will become available to all of your @Test methods since JUnit automatically creates an instance of test class for each @Test method run so @Before is not ALWAYS needed.

But with Approach # 1 ( declaration plus initialization both as instance fields ) , you are unnecessary tying up your test class instance creation and test data preparation while keeping concerns separate and visualizing the process in terms of stages is a good idea like , Create Instance of Test class -> Create Instance of SUT ( Service Under Test ) -> Prepare Test Data for SUT -> Call Test Method etc... ( Just a sample ).

@Before approach was a necessity with usage of mocking frameworks too as mocks needed to be initialized first in @Before method but that doesn't seem a necessity anymore . See

You have to realize that example put forward by you is very simple while in enterprise applications , classes to be unit tested are very complicated and folks don't want to clutter their field declaration area with test data preparation.

All in all - @Before is not mandatory for a reason - i.e. you use it if you wish to do certain things after creation of test class instance ( if that use case really fits in your scenario ) otherwise you are free to prepare your test data as instance fields.

Sabir Khan
  • 9,826
  • 7
  • 45
  • 98
-1

Here is an example:

The method annotated @Before runs before each call to @Test

So what I would do to solve your problem is to convert the local variable a field. In that way, the variable is accessible to each test methods.

 public class PawnTest { //The Test Class itself

 Board board;
 private Pawn p1;
@Before
public void setup() {
    board = new Board();
    p1 = new Pawn(Player.UP);
}


@Test
/**
 * Testing the right movement
 */
public void correctMovementTest() {
    board.placePiece(4, 3, p1);
    board.movePieceTo(5, 3, p1);
    assertEquals(board.getPiece(5, 3), p1);
}

@Test
/**
 * Testing the right movement
 */
public void correctMovementTest2() {
    board.placePiece(4, 3, p1);
    board.movePieceTo(6, 3, p1);
    assertEquals(board.getPiece(6, 3), p1);
}

@After
public void tearDown() {
    p1 = null;
    board = null;
}

Added tear down so that the test cases are independent of each other.

EDIT: Moved board initialization to setUp()

NewUser
  • 3,729
  • 10
  • 57
  • 79
  • While you're editing it, add some context to this code. Code-dump answers are not useful to anyone. – Joe C Sep 21 '17 at 06:36
  • 1
    The tests are now also dependent on each other because they share the same board. - ah, just saw that this is already a problem in OPs question – Absurd-Mind Sep 21 '17 at 06:37
  • @Absurd-Mind : I don't think it does. Correct me if I am wrong. p1 is reinitialized every time a test runs. – NewUser Sep 21 '17 at 06:41
  • @NewUser but board is only initialized once when the PawnTest is initialized. The same board object will be used in both tests. – Absurd-Mind Sep 21 '17 at 06:41
  • @Absurd-Mind. AH.. I was completely focused on the p1 object. Did not even relaize this. Thanks. – NewUser Sep 21 '17 at 07:02