David Kubecka
11/22/2024, 3:42 PMassertThat(actual).usingRecursiveComparison().ignoringFields("my.nested.field", MyClass::otherField.name).isEqualTo(expected)
but that is unsatisfactory for two reasons:
• The ignore list is either not type-safe (plain strings) or is too verbose (property references)
• The ignore list needs to be specified on the call site instead of on the declaration side.
The latter point is important in the case of test fixtures which are shared among many tests and which contain some dynamic or computed data, such as dates or hashes. In such cases it would be much more convenient for me if I could ignore the field "on the declaration side, e.g. like this:
my TEST_DATA = MyClass(fixedField = TEST_VALUE, dynamicField = any())
and then use some special assertion function that understands that any()
.
For that to work, I imagine that the return type of any()
should be both a subclass of the dynamicField
type as well some marker type for the assertion function. E.g. in pseudo code
interface AnyMarker
inline fun <reified T> any() = object : T, AnyMarker {}
This looks very similar to how Spring creates proxy classes. That relies on the all-open compiler plugin. But I can't use a similar trick here because
• It would be quite impractical to mark a class with some annotation for the plugin only for the testing use case
• I'm not even sure I can "open" classes such as String
which would be needed for the important ignore-hash use case.
My questions are:
• Does this idea make sense?
• Is it feasible? If yes, how to effectively implement any()
(or something similar)?mbonnin
11/29/2024, 2:42 PM@Test
fun test() = runTest {
CoroutineScope(Dispatchers.Default).launch {
delay(10.seconds)
println("done")
}
}
JVM: completes immediately, without printing "done".
JS: takes 10s and prints done.
Is there a fundamental reason this is like this? Could JS just ignore the launched coroutine? (Or if it's a problem because it has side effects for other tests, shouldn't the bahaviour be the same for JVM?)Romain
12/06/2024, 1:50 AMDavid Kubecka
12/09/2024, 3:52 PMimport org.junit.jupiter.params.provider.Arguments
data class TestData<G, E>(val testName: String, val given: G, val expected: E) {
override fun toString() = testName
}
fun <G, E> List<TestData<G, E>>.asArguments() = map { Arguments.of(it) }
fun myTestMethodTestData() = listOf(
TestData("test case 1", 1, "1"),
TestData("test case 2", 2, "2"),
).asArguments()
This works fine but isn't type-safe, i.e. nothing prevents me from writing
fun myTestMethodTestData() = listOf(
TestData("test case 1", 1, "1"),
TestData("test case 2", "2", 2),
).asArguments()
Is there any way how that can be achieved?
At first I thought I could utilize @NoInfer
to force the user to specify the type arguments explicitly, e.g. asArguments<Int, String>()
, but of course that doesn't enforce anything about the types in the TestData
class instance...martmists
12/14/2024, 3:35 PMPiotr Krzemiński
01/15/2025, 6:30 AMKrishna Prasad
01/16/2025, 11:07 PMaherbel
02/07/2025, 1:34 AMjean
03/04/2025, 11:03 PMDispatchers.setMain(UnconfinedTestDispatcher(testScheduler))
in my test function, it works. But if I use a Rule instead to do it, it fails. I do create the viewmodel from withing the test function and I do have the @get:Rule
as the first thing in the test class. I really don’t understand what I’m doing wrong here. Any idea on what I am missing?Piero Silvestri
03/06/2025, 9:41 AMMeika
03/07/2025, 4:22 AMcommonMain
. right now, we’re leaning towards using Mokkery since it makes migration easier syntax-wise. however, it still requires us to annotate the classes for mocking.
crossposting from #C3PQML5NU since it also make sense to ask hereEd Holloway-George
03/26/2025, 12:02 PM```class FakeRepo: Repo {
private val users = mutableSetOf<String>()
fun addUser(name: String) {
users.add(name)
}
fun getUserCount(): Int {
return users.size
}
}
class SomeService(val repo: Repo) {
fun addUser(name: String) {
userRepository.addUser(name)
}
fun getUserCount(): Int {
return userRepository.getUserCount()
}
}```
If you write tests in this form:
```class SomeServiceTest {
private val repo = FakeRepo()
private val sut = SomeService(repo)
@Test
fun test1() {
service.registerUser("Alice")
val result = sut.getUserCount()
assertEquals(1, result)
}
@Test
fun test2() {
service.registerUser("Bob")
service.registerUser("Charlie")
val result = sut.getUserCount()
assertEquals(2, result)
}
}```
Each individual test will pass if it executes first, but wont pass if it's executed after the other. As the order tests are run isn't guaranteed, this is a common cause of issues with fakes that hold state.Am I right here? The correct approach would be to init
sut
in a @BeforeTest
method right? One of my colleagues mentioned FakeRepo
would be recreated every time without @BeforeTest
Maiko Trindade
04/16/2025, 4:37 PMNino
04/18/2025, 12:24 PMlabel
and other stuff has great value to test.
I would like to avoid overriding the equal function of the data class for obvious reasons.
Any ideas ?
// Source code
data class Foo(
val label: String,
val onClicked: () -> Unit,
val onValueChanged: (Int) -> Unit,
)
object FooMapper {
fun map(
label: String,
viewModel: FooViewModel,
) = Foo(
label = label,
onClicked = { viewModel.onFooClicked() },
onValueChanged = { viewModel.onValueChanged(it.toLong()) },
)
}
class FooViewModel {
fun onFooClicked() { /* whatever */ }
fun onValueChanged(value: Long) { /* whatever */ }
}
// Unit tests
class FooMapperTest {
@Test
fun `foo map`() {
// Given
val label = "label"
val fooViewModel = mockk<FooViewModel>()
// When
val result = FooMapper.map(label = label, viewModel = fooViewModel)
// Then
// !! What should I use there?
assertEquals(
Foo(label = label, onClicked = {}, onValueChanged = {}),
result
)
}
}
Jonathan
06/09/2025, 3:39 PMSergey Dmitriev
06/27/2025, 12:49 PMParameterizedTest
?Luv Kumar
07/11/2025, 12:08 PMassertReflectionEquals
in java), i found shouldBeEqualToComparingFields
in kotest assertions module but that is also jvm only.ursus
07/16/2025, 10:27 PMkotlin-powerAssert = { id = "org.jetbrains.kotlin.plugin.power-assert", version.ref = "kotlin" }
plugins {
...
alias(libs.plugins.kotlin.powerAssert)
}
import kotlin.test.Test
class ExampleUnitTest {
@Test
fun testFunction() {
val hello = "Hello"
val world = "world!"
assert(hello.length == world.length)
}
}
... No matter what I do, I'm getting just the basic
Assertion failed
java.lang.AssertionError: Assertion failed
at foo.bar.ExampleUnitTest.testFunction(ExampleUnitTest.kt:11)
at java.base/java.lang.reflect.Method.invoke(Method.java:569)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
and not the right messages
what am I doing wrong?Alex Kuznetsov
07/23/2025, 5:21 PMmbonnin
07/26/2025, 1:17 PMmaarten ha
08/09/2025, 1:20 PMEugen Martynov
08/11/2025, 6:17 AMGenerated
word inside?joseph_ivie
08/15/2025, 4:35 PMassume()
?
Granted I'd prefer different naming, but that's less important than having it supported.edwinRNDR
08/17/2025, 8:18 AMkotlin.test.assertEquals(Double.NaN, Double.NaN, 1e-6)
to fail because Double.NaN != Double.NaN
ursus
08/21/2025, 7:24 PMclass FooTest {
private val myDependency = Dependency(StandardTestDispatcher(testScheduler)) <----
@Test fun foo() = runTest {
// val dispatcher = StandardTestDispatcher(testScheduler)
...
}
}
I like to construct dependencies to the sut in properties, but testScheduler
is only available from inside the runTest
lambda, which is "too late" for me.
How do I access testScheduler
from a property?jessewilson
09/04/2025, 9:03 PMjessewilson
09/09/2025, 3:40 PM@Test
fun happyPath() = runTest {
val helloWorld = document.createElement("div").apply {
appendElement("h1") {
appendText("hello world")
}
}
snapshotter.snapshot(helloWorld, "happyPath", Frame.None)
}
Oliver.O
09/11/2025, 1:15 PMdave08
09/11/2025, 1:54 PM