Hi, I am trying to integrate paparazzi in our proj...
# squarelibraries
e
Hi, I am trying to integrate paparazzi in our project, but I am running into two issues, possibly because we're doing things the wrong way in our project. Describing them takes a lot of text, so I will do it in a thread 🧵
One of the issues is that we have
android.util.Log.x
invocations in our tests that now fail. The error is the following:
Copy code
java.lang.UnsatisfiedLinkError: 'int android.util.Log.println_native(int, int, java.lang.String, java.lang.String)'
	at android.util.Log.println_native(Native Method)
	at android.util.Log.d(Log.java:156)
It's worth nothing that we used to load our own
android.util.Log
class in the test classpath and just call
println()
in our implementation, but with that present and adding the paparazzi plugin, the screenshot tests don't even run, Gradle fails with the following:
Copy code
JNIHelp F 11-25 18:20:30 <tel:880894843899|88089 4843899> java.lang.NoSuchMethodError: Method 'boolean android.util.Log.isLoggable(java.lang.String, int)' name or signature does not match
JNIHelp F 11-25 18:20:30 <tel:880894843899|88089 4843899> RegisterNatives failed for 'android/util/Log'; aborting...
So I commented out our
android.util.Log
class. The other error that comes up is:
Copy code
Can't create handler inside thread Thread[Test worker,5,main] that has not called Looper.prepare()
java.lang.RuntimeException: Can't create handler inside thread Thread[Test worker,5,main] that has not called Looper.prepare()
	at android.os.Handler.<init>(Handler.java:227)
	at android.os.Handler.<init>(Handler.java:129)
	at android.os.CountDownTimer$1.<init>(CountDownTimer.java:129)
	at android.os.CountDownTimer.<init>(CountDownTimer.java:129)
The first error I worked around by mocking
Log
with mockk, and I guess I could do something similar for the second error, but I feel like I am just patching the symptoms of a bigger problem. Another solution I see is just to create a separate module only for the screenshot tests, but that's kind of annoying, since we have several modules xD. Anyway, from what I could figure out, these issues arise because
layoutlib
pulls in a reimplementation of the Android framework, and that conflicts somehow with our tests. Not sure if this is something commonly encountered, but does anyone have some suggestions on how to best approach the above issues? 🙏
l
It may be good in this case to replace your Log calls with a logging library (I often use Kermit). Most logging libraries will let you configure the loggers. If using Kermit, you can call
Logger.setWriters(CommonWriter())
e
I agree, ideally we'd be using a logging library, but it's not feasible changing this right now. Meanwhile, I worked around the two issues above, but there's actually a third one I didn't notice before. We are updating the application locale at runtime using
android.content.res.Resources#updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics)
, we had everything mocked in tests with MockK, but after adding Paparazzi the mocking doesn't work anymore, there are two errors we see:
Copy code
java.lang.UnsatisfiedLinkError: 'int android.os.SystemProperties.native_get_int(java.lang.String, int)'
	at android.os.SystemProperties.native_get_int(Native Method)
	at android.os.SystemProperties.getInt(SystemProperties.java:180)
	at android.util.DisplayMetrics.getDeviceDensity(DisplayMetrics.java:424)
	at android.util.DisplayMetrics.<clinit>(DisplayMetrics.java:220)
	at jdk.internal.reflect.GeneratedSerializationConstructorAccessor17.newInstance(Unknown Source)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
	at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:48)
	at io.mockk.proxy.jvm.ObjenesisInstantiator.instanceViaObjenesis(ObjenesisInstantiator.kt:75)
	at io.mockk.proxy.jvm.ObjenesisInstantiator.instantiateViaProxy(ObjenesisInstantiator.kt:66)
	at io.mockk.proxy.jvm.ObjenesisInstantiator.instance(ObjenesisInstantiator.kt:29)
	at io.mockk.impl.instantiation.JvmInstantiator$instantiate$2.invoke(JvmInstantiator.kt:16)
	at io.mockk.impl.instantiation.AbstractInstantiator.instantiateViaInstanceFactoryRegistry(AbstractInstantiator.kt:17)
	at io.mockk.impl.instantiation.JvmInstantiator.instantiate(JvmInstantiator.kt:15)
	at io.mockk.impl.recording.states.RecordingState$matcher$signatureValue$1$1.invoke(RecordingState.kt:49)
	at io.mockk.impl.instantiation.JvmAnyValueGenerator$anyValue$2.invoke(JvmAnyValueGenerator.kt:35)
	at io.mockk.impl.instantiation.AnyValueGenerator.anyValue(AnyValueGenerator.kt:34)
	at io.mockk.impl.instantiation.JvmAnyValueGenerator.anyValue(JvmAnyValueGenerator.kt:31)
	at io.mockk.impl.recording.states.RecordingState$matcher$signatureValue$1.invoke(RecordingState.kt:48)
	at io.mockk.impl.recording.JvmSignatureValueGenerator.signatureValue(JvmSignatureValueGenerator.kt:20)
	at io.mockk.impl.recording.states.RecordingState.matcher(RecordingState.kt:47)
	at io.mockk.impl.recording.CommonCallRecorder.matcher(CommonCallRecorder.kt:52)
	at AppTest$fakeApp$1$getResources$1$3.invoke(AppTest.kt:73)
	at AppTest$fakeApp$1$getResources$1$3.invoke(AppTest.kt:43)
	at io.mockk.impl.eval.RecordedBlockEvaluator$record$block$1.invoke(RecordedBlockEvaluator.kt:25)
	at io.mockk.impl.eval.RecordedBlockEvaluator$enhanceWithRethrow$1.invoke(RecordedBlockEvaluator.kt:78)
	at io.mockk.impl.recording.JvmAutoHinter.autoHint(JvmAutoHinter.kt:23)
	at io.mockk.impl.eval.RecordedBlockEvaluator.record(RecordedBlockEvaluator.kt:40)
	at io.mockk.impl.eval.EveryBlockEvaluator.every(EveryBlockEvaluator.kt:30)
	at io.mockk.MockKDsl.internalEvery(API.kt:93)
	at io.mockk.MockKKt.every(MockK.kt:98)
	at AppTest$fakeApp$1.getResources(AppTest.kt:43)
and
Copy code
java.lang.NoClassDefFoundError: Could not initialize class android.util.DisplayMetrics
	at jdk.internal.reflect.GeneratedSerializationConstructorAccessor17.newInstance(Unknown Source)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
	at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:48)
	at io.mockk.proxy.jvm.ObjenesisInstantiator.instanceViaObjenesis(ObjenesisInstantiator.kt:75)
	at io.mockk.proxy.jvm.ObjenesisInstantiator.instantiateViaProxy(ObjenesisInstantiator.kt:66)
	at io.mockk.proxy.jvm.ObjenesisInstantiator.instance(ObjenesisInstantiator.kt:29)
	at io.mockk.impl.instantiation.JvmInstantiator$instantiate$2.invoke(JvmInstantiator.kt:16)
	at io.mockk.impl.instantiation.AbstractInstantiator.instantiateViaInstanceFactoryRegistry(AbstractInstantiator.kt:17)
	at io.mockk.impl.instantiation.JvmInstantiator.instantiate(JvmInstantiator.kt:15)
	at io.mockk.impl.recording.states.RecordingState$matcher$signatureValue$1$1.invoke(RecordingState.kt:49)
	at io.mockk.impl.instantiation.JvmAnyValueGenerator$anyValue$2.invoke(JvmAnyValueGenerator.kt:35)
	at io.mockk.impl.instantiation.AnyValueGenerator.anyValue(AnyValueGenerator.kt:34)
	at io.mockk.impl.instantiation.JvmAnyValueGenerator.anyValue(JvmAnyValueGenerator.kt:31)
	at io.mockk.impl.recording.states.RecordingState$matcher$signatureValue$1.invoke(RecordingState.kt:48)
	at io.mockk.impl.recording.JvmSignatureValueGenerator.signatureValue(JvmSignatureValueGenerator.kt:20)
	at io.mockk.impl.recording.states.RecordingState.matcher(RecordingState.kt:47)
	at io.mockk.impl.recording.CommonCallRecorder.matcher(CommonCallRecorder.kt:52)
	at AppTest$fakeApp$1$getResources$1$3.invoke(AppTest.kt:73)
	at AppTest$fakeApp$1$getResources$1$3.invoke(AppTest.kt:43)
	at io.mockk.impl.eval.RecordedBlockEvaluator$record$block$1.invoke(RecordedBlockEvaluator.kt:25)
	at io.mockk.impl.eval.RecordedBlockEvaluator$enhanceWithRethrow$1.invoke(RecordedBlockEvaluator.kt:78)
	at io.mockk.impl.recording.JvmAutoHinter.autoHint(JvmAutoHinter.kt:23)
	at io.mockk.impl.eval.RecordedBlockEvaluator.record(RecordedBlockEvaluator.kt:40)
	at io.mockk.impl.eval.EveryBlockEvaluator.every(EveryBlockEvaluator.kt:30)
	at io.mockk.MockKDsl.internalEvery(API.kt:93)
	at io.mockk.MockKKt.every(MockK.kt:98)
	at AppTest$fakeApp$1.getResources(AppTest.kt:43)
Needless to say, I didn't manage to find a workaround this time 😞
j
@Eduard Boloș hi, we face the same issue with paparazzi and mockk in the same module - did you find any solution? thx
e
Hi @Jan Skrasek! Our solution was to introduce abstractions so that we don't have to mock any Android classes in unit tests 🙃
j
👍 Thx, makes sense. I've opened ticket since I face also different issues: https://github.com/cashapp/paparazzi/issues/918
788 Views