CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-jetbrains-kotlin--kotlin-test-common

Common test assertions and utilities for Kotlin multiplatform projects

Pending
Overview
Eval results
Files

test-annotations.mddocs/

Test Annotations

Platform-agnostic test annotations that map to appropriate testing frameworks on each Kotlin compilation target. These annotations provide a unified way to mark test functions and test lifecycle methods across JVM, JavaScript, Native, and other Kotlin platforms.

Capabilities

@Test

Marks a function as a test case that should be executed by the testing framework.

/**
 * Marks a function as a test case.
 * The annotated function will be discovered and executed by the testing framework.
 * On JVM, maps to JUnit's @Test or TestNG's @Test.
 * On JavaScript, integrates with Mocha, Jest, or other JS testing frameworks.
 * On Native, works with the native testing infrastructure.
 */
@Target(AnnotationTarget.FUNCTION)
annotation class Test

Usage Examples:

import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class UserServiceTest {
    
    @Test
    fun testCreateUser() {
        val userService = UserService()
        val user = userService.createUser("Alice", "alice@example.com")
        
        assertEquals("Alice", user.name)
        assertEquals("alice@example.com", user.email)
        assertTrue(user.id > 0)
    }
    
    @Test
    fun testValidateEmail() {
        val userService = UserService()
        
        assertTrue(userService.isValidEmail("test@example.com"))
        assertFalse(userService.isValidEmail("invalid-email"))
        assertFalse(userService.isValidEmail(""))
    }
    
    @Test
    fun testUserAgeValidation() {
        val userService = UserService()
        
        // Test valid ages
        assertTrue(userService.isValidAge(18))
        assertTrue(userService.isValidAge(65))
        
        // Test invalid ages
        assertFalse(userService.isValidAge(-1))
        assertFalse(userService.isValidAge(0))
        assertFalse(userService.isValidAge(150))
    }
}

@Ignore

Marks a test or test class as ignored, preventing it from being executed.

/**
 * Marks a test function or test class as ignored.
 * Ignored tests are not executed but are typically reported as skipped.
 * Can be applied to individual test functions or entire test classes.
 * On JVM, maps to JUnit's @Ignore or TestNG's enabled=false.
 * On JavaScript and Native, integrates with platform-specific skip mechanisms.
 */
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class Ignore

Usage Examples:

import kotlin.test.*

class DatabaseTest {
    
    @Test
    fun testDatabaseConnection() {
        // This test runs normally
        val connection = Database.connect()
        assertTrue(connection.isConnected())
    }
    
    @Test
    @Ignore // Temporarily disable this test
    fun testSlowDatabaseQuery() {
        // This test is skipped during execution
        val results = Database.executeSlowQuery()
        assertTrue(results.isNotEmpty())
    }
    
    @Test
    @Ignore // Disable until bug #123 is fixed
    fun testBuggyFeature() {
        // This test won't run until the @Ignore is removed
        val result = BuggyService.processData()
        assertNotNull(result)
    }
}

// Ignore an entire test class
@Ignore
class ExperimentalFeatureTest {
    
    @Test
    fun testExperimentalFeature1() {
        // None of the tests in this class will run
    }
    
    @Test
    fun testExperimentalFeature2() {
        // This is also ignored due to class-level annotation
    }
}

class PartiallyDisabledTest {
    
    @Test
    fun testWorkingFeature() {
        // This test runs normally
        assertTrue(WorkingService.isAvailable())
    }
    
    @Test
    @Ignore // Only this specific test is ignored
    fun testBrokenFeature() {
        // This specific test is skipped
        BrokenService.process()
    }
}

@BeforeTest

Marks a function to be invoked before each test method in the class.

/**
 * Marks a function to be invoked before each test method execution.
 * The annotated function is called once before every @Test method in the same class.
 * Used for test setup, initialization, and preparing test fixtures.
 * On JVM, maps to JUnit's @Before or TestNG's @BeforeMethod.
 * On JavaScript and Native, integrates with platform-specific setup mechanisms.
 */
@Target(AnnotationTarget.FUNCTION)
annotation class BeforeTest

@AfterTest

Marks a function to be invoked after each test method in the class.

/**
 * Marks a function to be invoked after each test method execution.
 * The annotated function is called once after every @Test method in the same class.
 * Used for test cleanup, resource disposal, and restoring test state.
 * On JVM, maps to JUnit's @After or TestNG's @AfterMethod.
 * On JavaScript and Native, integrates with platform-specific cleanup mechanisms.
 */
@Target(AnnotationTarget.FUNCTION)
annotation class AfterTest

Combined Setup/Teardown Examples:

import kotlin.test.*

class FileProcessorTest {
    private lateinit var tempFile: File
    private lateinit var processor: FileProcessor
    
    @BeforeTest
    fun setUp() {
        // Called before each individual test method
        tempFile = createTempFile("test", ".txt")
        processor = FileProcessor()
        
        // Initialize test data
        tempFile.writeText("Initial test content")
        
        println("Test setup completed for: ${getCurrentTestMethod()}")
    }
    
    @AfterTest
    fun tearDown() {
        // Called after each individual test method
        if (::tempFile.isInitialized && tempFile.exists()) {
            tempFile.delete()
        }
        
        processor.cleanup()
        
        println("Test cleanup completed for: ${getCurrentTestMethod()}")
    }
    
    @Test
    fun testFileRead() {
        // setUp() called automatically before this
        val content = processor.readFile(tempFile)
        assertEquals("Initial test content", content)
        // tearDown() called automatically after this
    }
    
    @Test
    fun testFileWrite() {
        // setUp() called automatically before this (fresh tempFile)
        processor.writeFile(tempFile, "New content")
        val content = tempFile.readText()
        assertEquals("New content", content)
        // tearDown() called automatically after this
    }
    
    @Test
    fun testFileAppend() {
        // setUp() called automatically before this (fresh tempFile)
        processor.appendToFile(tempFile, " - appended")
        val content = tempFile.readText()
        assertEquals("Initial test content - appended", content)
        // tearDown() called automatically after this
    }
}

class DatabaseConnectionTest {
    private lateinit var connection: DatabaseConnection
    private lateinit var testDatabase: String
    
    @BeforeTest
    fun initializeDatabase() {
        // Create a fresh test database for each test
        testDatabase = "test_db_${System.currentTimeMillis()}"
        connection = DatabaseConnection.create(testDatabase)
        connection.connect()
        
        // Set up test schema
        connection.execute("CREATE TABLE users (id INT, name VARCHAR(100))")
        connection.execute("INSERT INTO users VALUES (1, 'Test User')")
    }
    
    @AfterTest
    fun cleanupDatabase() {
        // Clean up after each test
        if (::connection.isInitialized && connection.isConnected()) {
            connection.execute("DROP TABLE IF EXISTS users")
            connection.disconnect()
        }
        
        // Remove test database
        DatabaseUtils.dropDatabase(testDatabase)
    }
    
    @Test
    fun testSelectUser() {
        val users = connection.query("SELECT * FROM users")
        assertEquals(1, users.size)
        assertEquals("Test User", users[0]["name"])
    }
    
    @Test
    fun testInsertUser() {
        connection.execute("INSERT INTO users VALUES (2, 'Another User')")
        val users = connection.query("SELECT * FROM users")
        assertEquals(2, users.size)
    }
    
    @Test
    @Ignore // Database is slow in CI environment
    fun testComplexQuery() {
        // This test is ignored but would still get setUp/tearDown if it ran
        val result = connection.query("SELECT COUNT(*) FROM users WHERE name LIKE '%Test%'")
        assertEquals(1, result[0]["count"])
    }
}

Execution Order and Lifecycle

The test annotations follow a predictable execution order:

  1. Class Initialization: Test class is instantiated
  2. @BeforeTest: Setup method is called
  3. @Test: Individual test method is executed
  4. @AfterTest: Cleanup method is called
  5. Repeat: Steps 2-4 repeat for each test method
class TestLifecycleExample {
    
    init {
        println("1. Test class constructor called")
    }
    
    @BeforeTest
    fun setup() {
        println("2. @BeforeTest: Setting up for test")
    }
    
    @Test
    fun testA() {
        println("3. @Test: Running testA")
    }
    
    @Test
    fun testB() {
        println("3. @Test: Running testB")  
    }
    
    @AfterTest
    fun cleanup() {
        println("4. @AfterTest: Cleaning up after test")
    }
}

// Output would be:
// 1. Test class constructor called
// 2. @BeforeTest: Setting up for test
// 3. @Test: Running testA
// 4. @AfterTest: Cleaning up after test
// 2. @BeforeTest: Setting up for test
// 3. @Test: Running testB
// 4. @AfterTest: Cleaning up after test

Platform-Specific Behavior

JVM Platform

  • @Test: Maps to JUnit 4/5 @Test or TestNG @Test
  • @Ignore: Maps to JUnit @Ignore or TestNG enabled=false
  • @BeforeTest: Maps to JUnit @Before/@BeforeEach or TestNG @BeforeMethod
  • @AfterTest: Maps to JUnit @After/@AfterEach or TestNG @AfterMethod

JavaScript Platform

  • @Test: Integrates with Mocha it(), Jest test(), or other JS testing frameworks
  • @Ignore: Uses framework-specific skip mechanisms (it.skip(), test.skip())
  • @BeforeTest/@AfterTest: Maps to beforeEach()/afterEach() hooks

Native Platform

  • @Test: Integrates with native testing infrastructure
  • @Ignore: Platform-specific test skipping
  • @BeforeTest/@AfterTest: Native setup/teardown mechanisms

Best Practices

Setup and Cleanup

Always pair resource acquisition with proper cleanup:

@BeforeTest
fun setup() {
    // Acquire resources
    database = TestDatabase.create()
    httpServer = TestServer.start(port = 8080)
}

@AfterTest  
fun cleanup() {
    // Always clean up, even if test fails
    database?.close()
    httpServer?.stop()
}

Ignore Usage

Use meaningful comments when ignoring tests:

@Test
@Ignore // TODO: Re-enable after fixing issue #456 - NullPointerException in edge case
fun testEdgeCase() {
    // Test implementation
}

Test Independence

Ensure tests don't depend on execution order:

@BeforeTest
fun resetState() {
    // Reset to known state before each test
    GlobalState.reset()
    TestData.clear()
}

Install with Tessl CLI

npx tessl i tessl/maven-org-jetbrains-kotlin--kotlin-test-common

docs

boolean-assertions.md

collection-assertions.md

equality-assertions.md

exception-testing.md

framework-integration.md

index.md

test-annotations.md

test-utilities.md

type-null-assertions.md

tile.json