Skip to main content
HRTK supports four lifecycle annotations that let you run setup and teardown code at precise points in the test execution cycle. These work the same way as their JUnit counterparts, adapted for the Hytale runtime environment.

Hook Execution Order

For a suite with two tests, the execution order is:
@BeforeAll           (once, before any test)
  @BeforeEach        (before test 1)
    Test 1
  @AfterEach         (after test 1)
  @BeforeEach        (before test 2)
    Test 2
  @AfterEach         (after test 2)
@AfterAll            (once, after all tests)

@BeforeAll

Runs once before the first test in the suite. Must be static (or instance — HRTK supports both, but static is conventional).
import com.frotty27.hrtk.api.annotation.BeforeAll;
import com.frotty27.hrtk.api.annotation.HytaleSuite;

@HytaleSuite("Database Tests")
public class DatabaseTests {

    private static Object testData;

    @BeforeAll
    static void setupTestData() {
        testData = loadFixtures();
    }
}
Use @BeforeAll for expensive one-time setup like loading configuration files, initializing shared fixtures, or preparing test data structures.

@AfterAll

Runs once after the last test in the suite completes. Like @BeforeAll, it can be static or instance-level.
import com.frotty27.hrtk.api.annotation.AfterAll;

@HytaleSuite("Resource Tests")
public class ResourceTests {

    @AfterAll
    static void cleanupResources() {
        // Release any shared resources
        // This runs even if tests fail
    }
}

@BeforeEach

Runs before every test method in the suite. Always an instance method (not static). Use it to reset state between tests so they remain independent.
import com.frotty27.hrtk.api.annotation.BeforeEach;
import java.util.ArrayList;
import java.util.List;

@HytaleSuite("List Tests")
public class ListTests {

    private List<String> items;

    @BeforeEach
    void resetList() {
        items = new ArrayList<>();
        items.add("default");
    }

    @HytaleTest
    void testAddItem() {
        items.add("new");
        HytaleAssert.assertEquals(2, items.size());
    }

    @HytaleTest
    void testListStartsWithDefault() {
        // Fresh list from @BeforeEach -- not affected by testAddItem
        HytaleAssert.assertEquals(1, items.size());
        HytaleAssert.assertContains(items, "default");
    }
}

@AfterEach

Runs after every test method, regardless of whether the test passed or failed. Use it for cleanup.
import com.frotty27.hrtk.api.annotation.AfterEach;

@HytaleSuite("Connection Tests")
public class ConnectionTests {

    private Object connection;

    @BeforeEach
    void openConnection() {
        connection = createTestConnection();
    }

    @AfterEach
    void closeConnection() {
        if (connection != null) {
            closeTestConnection(connection);
            connection = null;
        }
    }
}

Complete Example

Here is a full lifecycle example that verifies the execution order using a counter:
import com.frotty27.hrtk.api.annotation.*;
import com.frotty27.hrtk.api.assert_.HytaleAssert;

@HytaleSuite("Lifecycle Tests")
public class LifecycleTests {

    private static int counter = 0;

    @BeforeAll
    static void beforeAll() {
        counter = 1;  // counter is now 1
    }

    @AfterAll
    static void afterAll() {
        counter++;    // final increment
    }

    @BeforeEach
    void beforeEach() {
        counter++;    // increments before each test
    }

    @AfterEach
    void afterEach() {
        counter++;    // increments after each test
    }

    @HytaleTest
    @Order(1)
    void firstTest() {
        // BeforeAll(1) + BeforeEach(2) = 2
        HytaleAssert.assertEquals(2, counter);
    }

    @HytaleTest
    @Order(2)
    void secondTest() {
        // Previous: 2, AfterEach(3), BeforeEach(4) = 4
        HytaleAssert.assertEquals(4, counter);
    }
}

Multiple Hooks

You can have multiple methods with the same lifecycle annotation. All of them will run, but the order between multiple @BeforeEach methods (for example) is not guaranteed.
@BeforeEach
void resetCounters() {
    hitCount = 0;
}

@BeforeEach
void resetTimers() {
    lastTickTime = 0;
}
If a lifecycle hook throws an exception, HRTK logs a warning and continues execution. The tests themselves are not automatically skipped when a hook fails — they may encounter unexpected state.

Next Steps