https://kotlinlang.org logo
#multiplatform
Title
# multiplatform
l

louiscad

10/27/2019, 9:32 AM
@h0tk3y I've had a reaaaaaly bad time with that combination: MPP + Android testing. It runs `androidTest`s as unit tests, which is something I don't want because these are actually non Android JVM and my code relies on Android runtime. I've tried to understand how MPP works with AGP, but failed to do so, and invested so much time to only get hacky workarounds, and they are not even sufficient. I ignored these issues for a long time but I want to automate the building, testing and publishing of my (open source) project with GitHub Actions, and these issues are becoming a true nightmare. I hate Android-less "unit test" so much now, and I still didn't figure out how to escape it when using MPP and having common tests and tests in
androidTest
(which maps to AGP
test
is seems, but I sadly don't know how). I'm mentioning you because I know you worked on AGP integration, and I digged so deep without a real solution that I don't know who else could help me on that one.
k

Kris Wong

10/27/2019, 2:51 PM
i found it pretty easy to get android unit tests up and running with robolectric. i imagine androidx.test would be similar. would that meet your needs?
l

louiscad

10/27/2019, 5:14 PM
There are some tests that I don't want to run on Roboelectric
I ended-up disabling all tasks having
DebugUnitTest
and
ReleaseUnitTest
in their name so I can focus on things that matter for the project.
a

ankushg

10/27/2019, 8:17 PM
I'm not sure how production-ready this is, but here's a project where I was able to get Android Instrumentation Tests to run via Espresso in an MPP: https://github.com/ankushg/MultiplatformParcelize/blob/master/multiplatform-parcelize/build.gradle#L13
I had to manually specify the sourcesets to AGP, and opted to use
androidTest
for Android unit tests (so it's similar to the other platforms) and create an
androidInstrumentationTest
sourceset for Espresso/Android Framework tess
s

Sebastian Sellmair [JB]

10/27/2019, 8:39 PM
Why is the
androidAndroidTest
sourceSet not working for you guys? I just have issues starting those from the ide, but running those Android tests from gradlew with
./gradlew connectedAndroidTest
just works fine for me!
The android default source set names are still a little confusing. I think the “androidInstrumentationTest” is a better name than “androidAndroidTest” But the logic right now also makes kind of sense: ${target}Main ${target}Test ${target}AndroidTest
(where target=android)
l

louiscad

10/28/2019, 7:10 AM
I have code in
commonTest
, and it gets run as local Android unit tests, while I want it as instrumented test.
s

Sebastian Sellmair [JB]

10/28/2019, 7:25 AM
Aaah got it!
In that case, maybe HMPP can help you there! Instead of having the tests in “commonTest” you could have a sourceSet where androidAndroidTest and others dependOn (except androidTest)
h

h0tk3y

10/28/2019, 1:05 PM
Currently, tests from
commonTest
are compiled and run as both Android unit tests and Android instrumented tests. This may actually be not the right way, and we'll reconsider this and maybe leave only one opiton. In unit tests, you could exclude all the tests from your
commonTest
by specifying an
exclude
patterrn.
As for tests declared in
src/androidTest/kotlin
, we have a nasty naming clash here that we are going to fix. Basically,
src/androidTest/kotlin
sources are the unit test sources, because the Android
test
source set gets a Kotlin source set counterpart with the name prefixed with the target name,
androidTest
. Similarly, the
androidTest
source set gets the Kotlin counterpart
androidAndroidTest
with the Kotlin sources in
src/androidAndroidTest/kotlin
.
k

Kris Wong

10/28/2019, 1:23 PM
i mean you have to configure your source sets for both the multiplatform and Android plugins, so might as well name it whatever you want
androidInstrumentedTest
makes the most sense to me
🙏 1
1
❤️ 2
s

Sebastian Sellmair [JB]

10/28/2019, 1:26 PM
I would like that name too ☺️
Since the gradle task is called “connectedAndroidTest”, maybe calling the source set “androidConnectedTest” makes sense?
l

louiscad

10/28/2019, 3:40 PM
@h0tk3y Where should I specify this
exclude
pattern? Or do you know where I should look for documentation regarding that?
h

h0tk3y

10/28/2019, 3:56 PM
@louiscad This should work (Groovy DSL):
Copy code
tasks.withType(Test).all {
    exclude 'com/h0tk3y/androidtestinginvestigation/common/**'
}
The string I passed to
exclude '...'
is the package name prefix with dots replaced by slashes. This excludes test classes that are found under paths that match the pattern. The Android unit test tasks are just normal Gradle
Test
tasks, which are documented here: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html
l

louiscad

10/28/2019, 4:14 PM
@h0tk3y I translated that to Kotlin DSL using a lambda for the exclude, and printed the files, but all I'm getting is
class
files from an intermediate directory that lost the fact that the file is coming from a
common
source set or not, so I would need to hardcode it for each file, which is clearly not scalable for a 40+ modules project.
For reference, here's the Gradle Kotlin DSL snippet I used to learn that:
Copy code
tasks.withType<Test>().matching { "UnitTest" in it.name }.all {
    this.exclude {
        println("got: $it")
        false
    }
}
h

h0tk3y

10/28/2019, 4:17 PM
Yep, you would need a common package prefix for the tests which you want to exclude. And do you have any tests at all that you would like to run as Android JVM unit tests?
l

louiscad

10/28/2019, 4:20 PM
In the future, I would have "Android" JVM unit tests, but for now, I just want to not have broken tests that should not be broken, and start adding tests for the emulator (or actual devices when run locally) to ensure there's no regression in the libraries.
h

h0tk3y

10/28/2019, 4:25 PM
Then, for now, you could disable the unit tests tasks altogether, if I get it right.
https://youtrack.jetbrains.com/issue/KT-34662 is for the option to run
commonTest
tests as unit tests only or instrumented tests only.
If you need that, maybe you could dynamically ignore some of the tests based on whether some class is present that is placed in the
src/androidTest/kotlin
sources. For example, use JUnit
Assume
in your tests' superclass
@BeforeAll
method and check that the marker class which is only present in the unit tests is not loaded. Doesn't look pretty, but it may work until KT-34662 is done.
👍 1
Or do the same by declaring an
expect
function in
commonTest
and providing an implementation that finishes a test as ignored in the Android unit tests.
l

louiscad

10/28/2019, 5:00 PM
you could disable the unit tests tasks altogether
Yes, that's what I'm currently doing:
Copy code
tasks.whenTaskAdded {
    if("DebugUnitTest" in name || "ReleaseUnitTest" in name) {
        enabled = false // [Some comment linking to this Slack thread]
    }
}
👍 1
Using
assume(someExpression)
in a
@BeforeAll
function is a great idea!
5 Views