can't test my commonMain with jvm packages.
# multiplatform
c
can't test my commonMain with jvm packages.
m
What exactly are you trying to test? You cannot have any JVM packages inside commonMain if you have configured any non-JVM target like iOS for example.
c
Yes. but I need to test with it before export it be used as a common base.
should only limit the commonMain source code not the commonTest code.
m
For commonTest code the same rules apply.
c
That may not be right. if the test code is limited then the test can not be made.
your code can't be tested them.
m
@calidion If I understood you correctly. Let's assume you have 3 targets • JVM • Android • iOS That would mean that your
commonMain
won't be able to have a dependency on
jvm
only libs and the same goes for
commonTest
. If you want to write some test cases that are only available for the
jvm
target. You can write them in
jvmTest
and if you want to have access to specific libs that are only available on
jvm
you can import these libs in gradle for
jvmTest
source set. This way, in your
jvmTest
, you would have access to all code written in
commonMain
and libs that are only available in
jvm
. Hope that answer your questions!
1
c
My test code is prepared for commonMain, why should I write tests in jvmTest instead of androidTest? commonMain defines the intefaces for jvm, android, iso to be used. Without a real implementation, common code can't be tested. And it should not be placed in jvm, android, ios.
m
My test code is prepared for commonMain, why should I write tests in jvmTest
Because you want to use
jvm
dependencies to test your code as per this
can't test my commonMain with jvm packages.
Am I misunderstanding your question?!
c
yes. jvm is optional , but the tests are essential to make my code fully tested.
m
Can we rephrase the question, I'm honestly not getting it
1
c
And I want all my tests accompanied with coverage report.
1. commonMain code is for all platform
2. interfaces for all platform to be implemented.
3. commonMain has invocation to these interfaces.
4. tests need to be done to test the interfaces are properly set.
5. you need one of the implementation from jvm, android, ios.
6. jvm may be the simplest option for implementation.
m
Let's tackle it one part at a time. • If you are looking for coverage report in KMP, you can use kotlinx-kover https://github.com/Kotlin/kotlinx-kover and if you want to get
JaCoCo
reports like we would do in
jvm
or
android
you can check this configuration from kotlinx-kover documentation https://kotlin.github.io/kotlinx-kover/gradle-plugin/#using-jacoco
commonMain
is where you can write Kotlin code that will be shared among all targets that you have already added in your Gradle file. If you wrote a class in it, it will automatically be available for all targets. But, if you need different implementation per platform (something like an interface and that interface implementation is implemented differently for each target) then you can use
expect/actual
feature of the KMP https://kotlinlang.org/docs/multiplatform-expect-actual.html
Did I understood your question correctly?
If not, please give me more details so I can better help you
c
How can I test interfaces inside the commonMain if it is not implemented? and how do I get the coverage report before jvm/android/ios code is written?
I think the process of written a source code is : 1. write some code 2. write some test 3. publish it as an api
so the process of writing commonMain code precedes the writing of androidMain code normally
Normally I must fully test the commonMain before I start to write androidMain or JvmMain code.
m
Interfaces define a contract, a set of methods that a class must implement. You can't test an interface as just an interface test. example, this code are all in
commonMain
Copy code
interface Animal {
    fun speak(): String
}

class Cat : Animal {
    override fun speak(): String {
        return "Newo Newo"
    }
}

class Dog : Animal {
    override fun speak(): String {
        return "Hao Hao"
    }
}
You won't be able to test the
Animal
interface. But you will be able to test the classes that are implement it, in this case, it would be
Cat
and
Dog
which will use the interface which will be added to the coverage report as being used. The other option, which is using
expect/actual
, example
Copy code
// commanMain
interface Animal {
    fun speak(): String
}

expect class Cat : Animal // This means that I your need to implement it in all supported targets (in this example, JVM, iOS)

// jvmMain
actual class Cat : Animal {
    override fun speak(): String {
        return "Newo Newo From JVM"
    }
}

// iosMain
actual class Cat : Animal {
    override fun speak(): String {
        return "Newo Newo From iOS"
    }
}
Then you can add your tests in
commonTest
like this
Copy code
expect class CatTests {
    @Test
    fun `test speak method`()
}
Then you go and implement the custom tests of the
CatTests
per platform like we did with implementation of the
Cat
class In
jvmMain
Copy code
actual class CatTests {
    @Test
    fun `test speak method`() {
        assertEquals("Newo Newo From JVM", Cat().speak())
    }
}
In
iosMain
Copy code
actual class CatTests {
    @Test
    fun `test speak method`() {
        assertEquals("Newo Newo From iOS", Cat().speak())
    }
}
c
can this code be tested? they are all in commonMain.
Copy code
interface In {
  fun api()
}

fun useInterface(i: In) {
   i.api()
}
m
Yes, with a 100% coverage report as well.
Copy code
class Tests {
    private val inImpl: In = object : In {
        override fun api() {
            // implement the logic you want here for testing purposes 
        }

    }
    
    @Test 
    fun `test useInterface method`() {
        useInterface(inImpl)
    } 
}
c
Ok. It means that I don't have to implement interface to test?
m
As per the example you shared with me, yes. This how you would implement tests for it.
c
OK. i Will try it later.
m
Great. Let me know how it goes
c
@Moussa But the problem is that only the implementation is jvm, android or ios. can test the logic.
Problems cannot be solved this way.
You can't make you code fully tested with out a real implementation.
so the commonTest code at lease should be enabled to use jvm code.
m
If your implementation of your interface is in
iosMain
,
jvmMain
, or
androidMain
then you implement your tests in two ways. The first way is to implement unit tests for each of them in their correct folder. Let's take your shared code as an example In
commanMain
Copy code
interface In {
  fun api()
}

fun useInterface(i: In) {
   i.api()
}
in
jvmMain
Copy code
class Moussa : In {
    override fun api() {
        // Implement your logic here
    }
}
Then you have to write test cases in
jvmTests
to test that specific logic and have coverage report for the part implemented in
jvmMain
OR, you can use
expect/actual
You would have in
commonTest
Copy code
expect class InTests {
    @Test
    fun `test api method per platform`()
}
Then you would go and implement that class and it's method in the test module of each platform. This way, make your code testable from
common
and allow access to your different implementation per platform and allow for the test coverage for all of them In
jvmTest
you would have
Copy code
actual class InTests {
    @Test
    actual fun `test api method per platform`() {
        // Implement your unit test here
    }
}