Test fixtures and lifecycle in Kotlin - Time & Space Complexity
When using test fixtures and lifecycle methods in Kotlin tests, it's important to see how setup and teardown steps affect the total time taken.
We want to know how the time grows as the number of tests increases.
Analyze the time complexity of the following Kotlin test lifecycle code.
class ExampleTest {
@BeforeEach
fun setup() {
// Prepare test data
}
@Test
fun testOne() {
// Test logic
}
@Test
fun testTwo() {
// Test logic
}
}
This code runs setup before each test method to prepare data, then runs the tests.
Look at what repeats when tests run.
- Primary operation: The setup method runs before each test.
- How many times: Once for each test method.
As the number of tests grows, the setup runs that many times too.
| Input Size (number of tests) | Approx. Setup Runs |
|---|---|
| 10 | 10 |
| 100 | 100 |
| 1000 | 1000 |
Pattern observation: The setup time grows directly with the number of tests.
Time Complexity: O(n)
This means the total setup time increases in a straight line as you add more tests.
[X] Wrong: "Setup runs only once no matter how many tests there are."
[OK] Correct: Setup marked with @BeforeEach runs before every test, so it repeats for each test method.
Understanding how test setup time grows helps you write efficient tests and shows you think about code performance in real projects.
"What if we change @BeforeEach to @BeforeAll? How would the time complexity change?"
Practice
Solution
Step 1: Understand what test fixtures do
Test fixtures set up the environment needed for tests and clean it afterward to avoid side effects.Step 2: Identify the correct purpose
Among the options, only preparing and cleaning the test environment matches the role of test fixtures.Final Answer:
To prepare and clean the test environment before and after tests -> Option AQuick Check:
Test fixtures = prepare and clean environment [OK]
- Confusing fixtures with test case logic
- Thinking fixtures generate test data
- Assuming fixtures compile code
Solution
Step 1: Recall lifecycle annotations
@BeforeAll runs once before all tests; @BeforeEach runs before each test.Step 2: Match annotation to description
The annotation that runs once before all tests is @BeforeAll.Final Answer:
@BeforeAll -> Option BQuick Check:
@BeforeAll = run once before all tests [OK]
- Mixing @BeforeEach with @BeforeAll
- Confusing @AfterAll with @BeforeAll
- Using @AfterEach instead of @BeforeAll
class SampleTest {
@BeforeAll
fun setupAll() = println("SetupAll")
@BeforeEach
fun setup() = println("Setup")
@Test
fun test1() = println("Test1")
@Test
fun test2() = println("Test2")
@AfterEach
fun tearDown() = println("TearDown")
@AfterAll
fun tearDownAll() = println("TearDownAll")
}Solution
Step 1: Understand lifecycle order
@BeforeAll runs once before all tests, then for each test @BeforeEach, test, @AfterEach run, and finally @AfterAll runs once after all tests.Step 2: Trace the print statements
Output order: SetupAll (once), then for test1: Setup, Test1, TearDown; for test2: Setup, Test2, TearDown; finally TearDownAll.Final Answer:
SetupAll Setup Test1 TearDown Setup Test2 TearDown TearDownAll -> Option DQuick Check:
Lifecycle order = D [OK]
- Assuming @BeforeEach runs once only
- Mixing order of AfterEach and AfterAll
- Forgetting @BeforeAll and @AfterAll run once
@BeforeAll
fun setup() {
println("Setup")
}
@Test
fun testExample() {
println("Test")
}Solution
Step 1: Recall @BeforeAll requirements in Kotlin
@BeforeAll methods must be static or placed inside a companion object to run once before all tests.Step 2: Check the code snippet
The setup() method is annotated with @BeforeAll but is not static nor inside a companion object, causing an error.Final Answer:
@BeforeAll method must be static or inside a companion object -> Option AQuick Check:
@BeforeAll requires static or companion object [OK]
- Thinking @BeforeAll can be a normal instance method
- Assuming test methods must be private
- Believing setup() needs a return value
Solution
Step 1: Understand lifecycle for shared resources
Resources like database connections should open once before all tests and close once after all tests to save time and avoid repeated setup.Step 2: Apply Kotlin @BeforeAll/@AfterAll rules
In Kotlin, @BeforeAll and @AfterAll must be inside a companion object or be static to run once per class.Step 3: Evaluate options
Use @BeforeAll inside a companion object to open and @AfterAll inside the companion object to close correctly uses @BeforeAll and @AfterAll inside a companion object for one-time setup and teardown.Final Answer:
Use @BeforeAll inside a companion object to open and @AfterAll inside the companion object to close -> Option CQuick Check:
Shared setup/teardown = @BeforeAll/@AfterAll in companion object [OK]
- Using @BeforeEach/@AfterEach for expensive shared setup
- Placing @BeforeAll/@AfterAll outside companion object
- Opening connection in constructor instead of fixture
Ready to prove it?
Pass the test and this pattern moves unknown
