Multiplatform testing framework providing unified API for writing tests across all Kotlin platforms with assertions and test annotations.
—
The core assertion interface that can be customized for different testing frameworks and platforms. The Asserter interface provides the foundation for all assertion operations in Kotlin Test.
The main interface for implementing custom assertion backends.
/**
* Abstracts the logic for performing assertions. Specific implementations can use
* JUnit, TestNG, or other assertion facilities.
*/
interface Asserter {
/**
* Fails the current test with the specified message
* @param message - The message to report
* @return Nothing (function never returns)
*/
fun fail(message: String?): Nothing
/**
* Fails the current test with the specified message and cause exception
* @param message - The message to report
* @param cause - The exception to set as the root cause of the reported failure
* @return Nothing (function never returns)
*/
fun fail(message: String?, cause: Throwable?): Nothing
/**
* Asserts that the specified value is true
* @param lazyMessage - Function to return a message to report if the assertion fails
* @param actual - Boolean value to check
*/
fun assertTrue(lazyMessage: () -> String?, actual: Boolean): Unit
/**
* Asserts that the specified value is true
* @param message - The message to report if the assertion fails
* @param actual - Boolean value to check
*/
fun assertTrue(message: String?, actual: Boolean): Unit
/**
* Asserts that the specified values are equal
* @param message - The message to report if the assertion fails
* @param expected - Expected value
* @param actual - Actual value
*/
fun assertEquals(message: String?, expected: Any?, actual: Any?): Unit
/**
* Asserts that the specified values are not equal
* @param message - The message to report if the assertion fails
* @param illegal - Value that should not match
* @param actual - Actual value
*/
fun assertNotEquals(message: String?, illegal: Any?, actual: Any?): Unit
/**
* Asserts that the specified values are the same instance
* @param message - The message to report if the assertion fails
* @param expected - Expected object reference
* @param actual - Actual object reference
*/
fun assertSame(message: String?, expected: Any?, actual: Any?): Unit
/**
* Asserts that the specified values are not the same instance
* @param message - The message to report if the assertion fails
* @param illegal - Object reference that should not match
* @param actual - Actual object reference
*/
fun assertNotSame(message: String?, illegal: Any?, actual: Any?): Unit
/**
* Asserts that the specified value is null
* @param message - The message to report if the assertion fails
* @param actual - Value to check for nullness
*/
fun assertNull(message: String?, actual: Any?): Unit
/**
* Asserts that the specified value is not null
* @param message - The message to report if the assertion fails
* @param actual - Value to check for non-nullness
*/
fun assertNotNull(message: String?, actual: Any?): Unit
}Interface for providing Asserter instances based on context.
/**
* Checks applicability and provides Asserter instance
*/
interface AsserterContributor {
/**
* Provides Asserter instance or null depending on the current context
* @return Asserter instance or null if not applicable
*/
fun contribute(): Asserter?
}Default implementation of the Asserter interface.
/**
* Default Asserter implementation to avoid dependency on JUnit or TestNG
*/
object DefaultAsserter : AsserterGlobal property to access the current asserter instance.
/**
* Current adapter providing assertion implementations
* Gets the current asserter or looks up an available one
*/
val asserter: Asserterimport kotlin.test.*
@Test
fun testUsingGlobalAsserter() {
// The global asserter is used automatically by assertion functions
val result = performCalculation()
// These functions delegate to the current asserter
assertTrue(result > 0)
assertEquals(42, result)
assertNotNull(result)
// You can also use the asserter directly (though not typically needed)
asserter.assertTrue("Result should be positive", result > 0)
asserter.assertEquals("Should equal 42", 42, result)
}class CustomAsserter(private val testName: String) : Asserter {
override fun fail(message: String?): Nothing {
val fullMessage = "[$testName] ${message ?: "Test failed"}"
throw AssertionError(fullMessage)
}
override fun fail(message: String?, cause: Throwable?): Nothing {
val fullMessage = "[$testName] ${message ?: "Test failed"}"
throw AssertionError(fullMessage, cause)
}
override fun assertTrue(message: String?, actual: Boolean) {
if (!actual) {
fail(message ?: "Expected value to be true")
}
}
override fun assertEquals(message: String?, expected: Any?, actual: Any?) {
if (expected != actual) {
fail("${message ?: "Values should be equal"}: expected <$expected>, actual <$actual>")
}
}
override fun assertNotEquals(message: String?, illegal: Any?, actual: Any?) {
if (illegal == actual) {
fail("${message ?: "Values should not be equal"}: illegal value <$actual>")
}
}
override fun assertSame(message: String?, expected: Any?, actual: Any?) {
if (expected !== actual) {
fail("${message ?: "References should be same"}: expected <$expected>, actual <$actual>")
}
}
override fun assertNotSame(message: String?, illegal: Any?, actual: Any?) {
if (illegal === actual) {
fail("${message ?: "References should not be same"}: illegal reference <$actual>")
}
}
override fun assertNull(message: String?, actual: Any?) {
if (actual != null) {
fail("${message ?: "Value should be null"}: actual <$actual>")
}
}
override fun assertNotNull(message: String?, actual: Any?) {
if (actual == null) {
fail(message ?: "Value should not be null")
}
}
}class LoggingAsserterContributor : AsserterContributor {
override fun contribute(): Asserter? {
// Only provide the logging asserter in debug mode
return if (isDebugMode()) {
LoggingAsserter()
} else {
null
}
}
}
class LoggingAsserter : Asserter {
private fun log(operation: String, message: String?) {
println("[ASSERT] $operation: ${message ?: "no message"}")
}
override fun fail(message: String?): Nothing {
log("FAIL", message)
throw AssertionError(message)
}
override fun fail(message: String?, cause: Throwable?): Nothing {
log("FAIL", "$message (caused by: ${cause?.message})")
throw AssertionError(message, cause)
}
override fun assertTrue(message: String?, actual: Boolean) {
log("ASSERT_TRUE", message)
if (!actual) {
throw AssertionError(message ?: "Expected true")
}
}
override fun assertEquals(message: String?, expected: Any?, actual: Any?) {
log("ASSERT_EQUALS", "$message: expected=$expected, actual=$actual")
if (expected != actual) {
throw AssertionError("Expected <$expected>, actual <$actual>")
}
}
// ... implement other methods with logging
}// This is how kotlin-test integrates with JUnit internally
class JUnitAsserter : Asserter {
override fun fail(message: String?): Nothing {
org.junit.Assert.fail(message)
throw AssertionError() // Never reached, but needed for Nothing return type
}
override fun assertTrue(message: String?, actual: Boolean) {
org.junit.Assert.assertTrue(message, actual)
}
override fun assertEquals(message: String?, expected: Any?, actual: Any?) {
org.junit.Assert.assertEquals(message, expected, actual)
}
// ... other methods delegate to JUnit assertions
}class TestNGAsserter : Asserter {
override fun fail(message: String?): Nothing {
org.testng.Assert.fail(message)
throw AssertionError() // Never reached
}
override fun assertTrue(message: String?, actual: Boolean) {
org.testng.Assert.assertTrue(actual, message)
}
override fun assertEquals(message: String?, expected: Any?, actual: Any?) {
org.testng.Assert.assertEquals(actual, expected, message)
}
// ... other methods delegate to TestNG assertions
}class MyFrameworkAsserter : Asserter {
override fun fail(message: String?): Nothing {
MyTestFramework.reportFailure(message ?: "Test failed")
throw MyTestFramework.TestFailedException(message)
}
override fun assertTrue(message: String?, actual: Boolean) {
if (!actual) {
MyTestFramework.reportAssertion("assertTrue", message, false)
fail(message ?: "Expected true")
} else {
MyTestFramework.reportAssertion("assertTrue", message, true)
}
}
// ... implement other methods to integrate with your framework
}@Test
fun testWithCustomAsserter() {
val originalAsserter = asserter
val customAsserter = CustomAsserter("MyTest")
try {
// Temporarily override the asserter
overrideAsserter(customAsserter)
// All assertions now use the custom asserter
assertTrue(true) // Will use CustomAsserter
assertEquals(42, 42) // Will use CustomAsserter
} finally {
// Restore original asserter
overrideAsserter(originalAsserter)
}
}
// Helper function to override asserter (internal API)
fun overrideAsserter(newAsserter: Asserter): Asserter {
// This uses internal kotlin-test API
return kotlin.test.overrideAsserter(newAsserter) ?: DefaultAsserter
}class ChainedAsserter(private val asserters: List<Asserter>) : Asserter {
override fun fail(message: String?): Nothing {
// Fail using the first asserter in the chain
asserters.firstOrNull()?.fail(message) ?: throw AssertionError(message)
}
override fun assertTrue(message: String?, actual: Boolean) {
// Execute assertion on all asserters in chain
for (asserter in asserters) {
asserter.assertTrue(message, actual)
}
}
// ... implement other methods to delegate to all asserters
}
@Test
fun testWithChainedAsserters() {
val loggingAsserter = LoggingAsserter()
val junitAsserter = JUnitAsserter()
val chainedAsserter = ChainedAsserter(listOf(loggingAsserter, junitAsserter))
overrideAsserter(chainedAsserter)
// This will both log the assertion AND use JUnit's assertion
assertTrue(5 > 3)
}⚠️ JVM Platform Only - This function is only available when running tests on the JVM platform.
Returns the current stack trace as an array of stack trace elements for debugging and diagnostic purposes.
/**
* Returns an array of stack trace elements, each representing one stack frame
* The first element represents the top of the stack (where currentStackTrace was called)
* @return Array of StackTraceElement representing the call stack
*/
inline fun currentStackTrace(): Array<StackTraceElement>Usage Examples:
import kotlin.test.*
@Test
fun testStackTraceCapture() {
// Capture stack trace at current location
val stackTrace = currentStackTrace()
// Examine the top frame (where currentStackTrace was called)
val topFrame = stackTrace[0]
println("Called from: ${topFrame.fileName}:${topFrame.lineNumber}")
println("Method: ${topFrame.methodName}")
println("Class: ${topFrame.className}")
// Verify we captured the right location
assertEquals("AssertionTests.kt", topFrame.fileName)
assertTrue(topFrame.lineNumber > 0)
}
// Custom diagnostic assertion using stack trace
fun assertWithLocation(condition: Boolean, message: String) {
if (!condition) {
val location = currentStackTrace()[0]
fail("$message at ${location.fileName}:${location.lineNumber}")
}
}
@Test
fun testCustomDiagnosticAssertion() {
val value = 42
// This will provide detailed location info if it fails
assertWithLocation(value == 42, "Value should be 42")
// Example of failure (commented out)
// assertWithLocation(value == 99, "This will show exact location")
}
// Advanced: Custom test framework integration
class DetailedAsserter : Asserter {
override fun fail(message: String?): Nothing {
val location = currentStackTrace()[1] // Skip this frame
throw AssertionError("$message\n at ${location.className}.${location.methodName}(${location.fileName}:${location.lineNumber})")
}
override fun assertTrue(message: String?, actual: Boolean) {
if (!actual) {
fail(message ?: "Assertion failed")
}
}
// ... implement other Asserter methods
}
@Test
fun testDetailedStackTraceDiagnostics() {
// This custom asserter will provide detailed location info
val detailedAsserter = DetailedAsserter()
try {
detailedAsserter.assertTrue("This should pass", true)
detailedAsserter.assertTrue("This will fail with location", false)
} catch (e: AssertionError) {
// The error message will include precise location information
assertContains(e.message ?: "", "AssertionTests.kt")
}
}Platform Notes:
currentStackTrace() was calledtodo() function for location reportingThis function is particularly useful for building custom assertion utilities, debugging test failures, and creating diagnostic tools that need to report precise location information.
Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlin--kotlin-test