Compose android tests fail when minify enabled, class / methods not found - android-jetpack-compose

My Compose unit tests fail if I have minify enabled on my app.
java.lang.NoSuchMethodError: No interface method complete()Z in class Lkotlinx/coroutines/CompletableJob; or its super classes (declaration of 'kotlinx.coroutines.CompletableJob' appears in /data/app/com.clover.engine-7ap2fJvfCgX89H0L0ytLZg==/base.apk!classes3.dex)
at androidx.compose.ui.test.junit4.IdlingResourceRegistry.<init>(IdlingResourceRegistry.jvm.kt:46)
at androidx.compose.ui.test.junit4.IdlingResourceRegistry.<init>(IdlingResourceRegistry.jvm.kt:37)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.<init>(ComposeUiTest.android.kt:220)
...
at androidx.compose.ui.test.junit4.AndroidComposeTestRule_androidKt.createComposeRule(AndroidComposeTestRule.android.kt:212)
at com.mytest.MyTest.<init>(...)
I've tried adding proguard rules for the missing symbols, which gets past the immediate problem, but it seems to be a never ending list which makes me think that isn't the right direction.
Minify is enabled like this:
buildTypes.all {
minifyEnabled true
// ...
}
I have other androidTest tests that run fine, and the Compose-based activity in the app works fine when minify is enabled.
My Compose-related dependencies are like this:
def composeBom = platform('androidx.compose:compose-bom:2023.1.00')
implementation composeBom
androidTestImplementation composeBom
implementation 'androidx.compose.material3:material3'
implementation 'androidx.compose.ui:ui-tooling-preview'
debugImplementation 'androidx.compose.ui:ui-tooling'
implementation 'androidx.compose.runtime:runtime-livedata'
implementation 'androidx.activity:activity-compose:1.6.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1'
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
EDIT: I was looking into the "Now in Android" Compose best-practice app. Not 100% clear but it seems like it also does not enabled minify for unit testing:
https://github.com/android/nowinandroid/blob/0d23e64848adea66ec878293a7c1bd18d128689a/app/build.gradle.kts#L46

Related

Have Spock fail a data-driven feature if an iteration fails

Is there a way to do the equivalent of #Stepwise's failure behavior for a single feature? We have some integration tests that are set up such that setupSpec() kicks off a Kafka process, and then the actual test checks that each step happened. If step 3 failed, there's no reason to bother checking subsequent steps.
There is no built-in way to do this, but assuming you are using a recent 2.x Spock version and not 1.3 or so, a relatively simple annotation-driven Spock extension can do the trick for you.
package de.scrum_master.stackoverflow.q71414311
import org.spockframework.runtime.extension.ExtensionAnnotation
import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#ExtensionAnnotation(StepwiseIterationsExtension)
#interface StepwiseIterations {}
package de.scrum_master.stackoverflow.q71414311
import org.spockframework.runtime.extension.IAnnotationDrivenExtension
import org.spockframework.runtime.model.FeatureInfo
import org.spockframework.runtime.model.parallel.ExecutionMode
class StepwiseIterationsExtension implements IAnnotationDrivenExtension<StepwiseIterations> {
#Override
void visitFeatureAnnotation(StepwiseIterations annotation, FeatureInfo feature) {
// Disable parallel iteration execution for #StepwiseIterations feature,
// similarly to how #Stepwise disables it for the whole specification
feature.setExecutionMode(ExecutionMode.SAME_THREAD)
// If an error occurs in this feature, skip remaining iterations
feature.getFeatureMethod().addInterceptor({ invocation ->
try {
invocation.proceed()
}
catch (Throwable t) {
invocation.getFeature().skip("skipping subsequent iterations after failure")
throw t
}
})
}
}
Add this to your code base, annotate your iterated test with #StepwiseIterations and run it. I think the result is exactly what you are looking for.
In Spock 1.3, an similar, but more complex extension would also be possible.
I also want to express my special thanks to Leonard Brünings, Spock maintainer and boundless source of knowledge. I had a more complex version of this extension in place, but after discussing with him, it evolved into this tiny, elegant solution we are seeing here.
FYI, there is a pre-existing Spock issue #1008 requesting this feature. I created pull request #1442 which adds this capability to #Stepwise. So hopefully in the future we do not need an extra annotation and extra extension anymore.

IDEA Intellij does not support Cucumber JVM World (Hooks) in Groovy

If I use cucumber.api.groovy.Hooks.World then tests are working fine. But I cannot open declaration in steps definition
World() {
def world = new LucieWorld()
world.metaClass.mixin Lucie
world
}
step definition:
Given(~/^User is (.+)$/) { def username ->
login(username) //I cannot open declaration here
}
When I am in a step definition file (groovy), Intellij can't seem to see variables and methods defined in the cucumber "World" object Lucie. So don't get IDE support (auto-complete, etc) for those which is a bit annoying. How can I fix that?
I try to use this.metaClass.mixin(Lucie) in step definition, but I think, that this is not a good solution.

How to achieve precompiler directive like functionality

I'm developing an angular app, and it's recommended to use generated code for a lot of things running in production, namely template caches, expression caches, and a static DI injector. There's currently no nice way to switch between different build configurations, so I'm using the pattern recommended here:
In lib/main.dart you can see initializer-prod.dart file being imported, which has initializer-dev.dart counterpart. Switching between those two file will allow you to switch between prod and dev modes. You will need to run the generator script before using the prod mode.
This results in the following import:
//import 'initializer_prod.dart' as init; // Use in prod/test.
import 'initializer_dev.dart' as init; // Use in dev.
As you can see, switching the import is a manual process. Is there a better, more automatic way to achieve this?
I see two possibilities (haven't tried any of these myself yet)
one is to use a transformer (see also Pass custom parameters to a dart application when using pub serve to run it)
or
Compile-time dead code elimination with dart2js
Recently a feature should have been added that pub build allows adding an environment variables using a command line option (like dart2js's -d)
log(String msg) {
if (const String.fromEnvironment('DEBUG') != null) {
print('debug: $msg');
}
}
main() {
log('In production, I do not exist');
}
Some links about transformers:
Can We Build It? Yes, We Can!
Assets and Transformers
Day 992: Search and Replace Dart Transformer to Hide from Polymer
Dart Transformers for Polymer Cleanup
Pub transformers
dart2js_dransformer.dart
Document user-defined transformers
EDIT
I was able to configure dart2js options in pubspec.yaml like
transformers:
- $dart2js:
commandLineOptions: [-DDEBUG=true]
environment:
DEBUG: "true"
suppressWarnings: true
terse: true
They are validate and pub build fails if an unknown option is provided or if it's not the expected format (yaml list for commandLineOptions, yaml map form environment)
BUT String.fromEnvironment() didn't get a value
According to this issue, this is supported:
Passing in arguments to dart2js during pub build
I filed a bug How to pass options to dart2js from pubspec.yaml
EDIT-2
I tried it and it is working now:
transformers: # or dev_transformers
- $dart2js:
environment: { PROD: "true" }
access it from the code like
String.fromEnvironment()
main() {
print('PROD: ${const String.fromEnvironment('PROD')}');
// works in the browser
// prints 'PROD: null' in Dartium
// prints 'PROD: true' in Chrome
}
see also Configuring the Built-in dart2js Transformer
EDIT-3
Another way is to use assert to set variables.
assert is ignored in production.

Running F# xUnit Fact from TestDriven.NET reporting "It looks like you're trying to execute an xUnit.net unit test."

I am trying to run xUnit tests (from an F# module, if it makes any difference) using TestDriven.NET, but whatever I do I get this error:
It looks like you're trying to execute an xUnit.net unit test.
For xUnit 1.5 or above (recommended):
Please ensure that the directory containing your 'xunit.dll' reference also contains xUnit's
test runner files ('xunit.dll.tdnet', 'xunit.runner.tdnet.dll' etc.)
For earlier versions:
You need to install support for TestDriven.Net using xUnit's 'xunit.installer.exe' application.
You can find xUnit.net downloads and support here:
http://www.codeplex.com/xunit
I tried following the suggestions, i.e. I copied the files
xunit.dll.tdnet
xunit.extensions.dll
xunit.gui.clr4.exe
xunit.runner.tdnet.dll
xunit.runner.utility.dll
xunit.runner.utility.xml
xunit.xml
to the folder with xunit.dll and I ran xunit.installer.exe. How can I get it to work?
I just figured out that I forgot to make the test a function in F# (so it was just a value). The error message can't be more misleading though!
You have two problems:
your Fact is broken:-
If you hover over the
please work
bit, you'll see something like: unit -> int
For a Fact to be picked up by an xUnit runner, it needs to yield `unit (void).
Hence, one key thing to get right first is to not return anything. In other words, replace your 123 with () (or an Assertion).
You can guard against this by putting a :unit stipulation on the test:-
[<Fact>]
let ``please work`` () : unit = 123
This will force a compilation error.
TestDriven.NET is reporting it cannot find the xunit.tdnet modules
It's critical to get step 1 right first. Then retry and the problem should be gone
If it remains...
Either try the VS-based runner which should work as long as it's installed and xunit.dll is getting to your output dir or look at the docs for your version of TD.NET for detailed troubleshooting notes (exec summary is if the .tdnet file was in your out dir or you undo and redo the xunit.installer from the folder containing the packages it should just work, esp if you are on latest)

"Test a little, code a little" in Grails project, using Easyb or Spock, in IntelliJ

My original issue is described perfectly by this post: I want to follow TDD:
write a small test
watch it fail
write just enough code to make it succeed
watch it succeed
repeat
I am working on a Grails project in IntelliJ. If all I want is to write normal JUnit tests, the above post solves everything:
Head to /test/unit
Put some test code in a "class Xyz extends GroovyTestCase" class
Hit Shift F10
JUnit report pops up within a second or two
The problem is that I would like to use one of the very cool "describe-in-english" testing setups, like Easyb or Spock.
What do I do? It would be magic to just start with the auto-generated Test class Grails makes for me, then cram Spock stuff into it. Obviously I can't use "extends" twice. Does this give the gist of what I'm trying to do though?
class Xyz extends GroovyTestCase extends spock.lang.Specification {
//void testSomething() {
// fail "Implement me"
//}
def "length of Spock's and his friends' names"() {
expect:
name.size() == length
where:
name | length
"Spock" | 5
"Kirk" | 4
"Scotty" | 6
}
}
Extend spock classes, not groovy's. You can choose from UnitSpec, ControllerSpec, IntegrationSpec and others as listed in source code. Spock will take care of the rest.
-Use "grails install-templates"
-Change the Tests templates to something along the lines of:
#artifact.package#
import grails.test.mixin.*
import org.junit.*
import spock.lang.Specification
#TestFor(#artifact.testclass#)
class #artifact.name# extends Specification {
}
-Write the test code in Spock (or normal JUnit code)
-IntelliJ->Run->Edit Configurations. Add New Configuration of the JUnit type. Test kind: <All in package/All in UnitTest directory/All in one UnitTest class/etc.>
-(Shortcut: Cursor over method name, or class name->ctrl-shift-F10)
On my original question:
In retrospect, I was getting hung-up on the "run all in directory" part of that blog post. Current IntelliJ DOES let you run all JUnit tests in a directory, or the entire project.
Once I understood that, the next step was to realize that Spock tests ARE JUnit tests. Make a "class Xyz extends Specification {}" class in the test/unit directory, fill it with Spock code, and Run as... JUnit. Magic!

Resources