I'm attempting to run non-instrumented tests using jetpack compose and robolectric. I've had some success in basic testing, for the most part tests work like their instrumented counter parts, but can't figure out how to mock a nested #Composeable function.
#Composable
fun ScoreboardTileTeam(
team: String = "Team Name",
) {
Text(team)
Logo() //This is the function I'd like to mock in my tests.
}
#Composable
fun Logo() {
Image(
painter = painterResource(R.drawable.logo),
contentDescription = "Logo"
)
}
Being able to see that the nested function was called and what parameters it was called with would fulfill my primary needs.
It seems that mockito and mockk are built around mocking classes by replacing/augmenting the class's sub methods, but with the #Composable annotation these functions appear to be stand alone.
Don't know if this is still relevant, but I made a tool for this recently: https://github.com/jeppeman/mockposable
You can use it like so:
mockkStatic("fqname.of.LogoKt")
everyComposable { Logo() } returns ...
Related
I want to discuss the options i have come across for calling a native method from a IOS ViewManager/View in JS side without using properties but direct method calls.
Option A:
implementing a method in the ViewManager which searches for the correct view and calls the given method in the view, like this:
func callMethodViaManager(_ node:NSNumber) {
DispatchQueue.main.async {
let myView = self.bridge.uiManager.view(forReactTag: node) as! MyView
myView.myMethod()
}
}
and then in the JS side implement a handler like this:
const handleSomething = (e) => {
UIManager.dispatchViewManagerCommand(
ReactNative.findNodeHandle(ref.current),
UIManager.SwiftComponent.Commands.callMethodViaManager,
[])
};
This is just a short summary of the relevant parts, the process in full can be seen in full detail maybe just a bit old but with some adjustments one can get it to work also with functional components:
https://medium.com/#jjdanek/react-native-calling-class-methods-on-native-swift-views-521faf44f3dc
Option B:
For this option let's go with the best scenario which is that one can get all the necessary data, setup, etc on the ViewManager ready via delegates for example or some other pattern or Swift sugar.
Calling the Native methods in the ViewManager from the JS side directly with the use of NativeModules, like this:
const handleSomething = (e) => {
NativeModules.MyViewManager.myMethod()
};
I could not find much about this option in correlation to a Native View being bridged, this way of doing it is used for Native Module bridging explicitly. The only things i could find where:
React Native UI Component Method
or guides like these one:
https://teabreak.e-spres-oh.com/swift-in-react-native-the-ultimate-guide-part-1-modules-9bb8d054db03#4377
I have tried both methods and at first glance they seem to work both.
So my questions are:
Why is Option A the better solution/option and the one which is recommended or most used?
What is wrong or can go wrong with Option B?
Is there anything else to take into consideration?
Is there a better way which is not trough properties?
Option B is more flexible.
If you use Option A, then you can't pass Promise and Callback parameter to the native side.
It seems possible in iOS But not in Android.
This is a related issue.
React Native bridge API is all confusing
There is no guide about how to call the native UI method with Promise or Callback.
There is a example about how to pass Promise to native side with calling native UI instance method.
SketchView... is just my example module name.
class SketchViewModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
override fun getName() = "SketchViewModule"
private fun runCommandWithPromise(
viewId: Int, promise: Promise, handler: (view: SketchView) -> Unit
) {
reactApplicationContext.getNativeModule(UIManagerModule::class.java)?.let { uiManager ->
uiManager.addUIBlock {
(it.resolveView(viewId) as? SketchView)?.let { view ->
handler(view)
} ?: run {
promise.safeReject("[runCommandWithPromise] Cannot find View with UIManager")
}
}
}
}
...
I'm currently studying Jetpack Compose in an attempt to build a feature-rich application using modern Android architecture components. Traditionally, each screen (or navigation unit) in my application would be either an activity or a fragment, each with its own lifecycle bindings, but with Jetpack Compose and the Compose Navigation library, I would do something like this:
MainActivity.kt:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "main") {
composable("main") { MainScreen(navController) }
// More composable calls
}
}
}
}
Where MainScreen is just a composable. My questions are:
What is the equivalent here of a "lifecycle" for this composable? Say I want to perform some action when the screen is loaded, when it is destroyed etc. This is perhaps more relevant to the case where I have more screens and navigation between them
Is there some way to integrate between Compose and standard activities? That is, define activities for screens as you would, with each activity being a ComponentActivity and defining its own composable layout? Is this discouraged for some reason?
The Compose application is designed to be used in a single-activity architecture with no fragments.
You can still have multiple activities or fragments and use setContent in each of them, but in this case the transfer of data between activities falls on your shoulders. Use this approach if you're adding new Compose screens to an existing application built the old way.
But with Compose, it's much easier to do all the navigation within a single activity using Compose Navigation. Much less code, better performance due to no unnecessary code layers, easy to transfer data, etc.
To work with the view lifecycle, check out compose side-effects:
LaunchedEffect can be used to execute an action when the view appears. It also runs on a coroutine context that is bound to the current composable: you can easily run suspend functions, and when the view disappears from view hierarchy - the coroutine will be canceled.
DisposableEffect can be used to subscribe to/unsubscribe from callbacks.
When you rotate the screen, all effects will restart no matter which key you passed.
#Composable
fun MainScreen(navController: NavController) {
LaunchedEffect(Unit) {
println("LaunchedEffect: entered main")
var i = 0
// Just an example of coroutines usage
// don't use this way to track screen disappearance
// DisposableEffect is better for this
try {
while (true) {
delay(1000)
println("LaunchedEffect: ${i++} sec passed")
}
} catch (cancel: CancellationException) {
println("LaunchedEffect: job cancelled")
}
}
DisposableEffect(Unit) {
println("DisposableEffect: entered main")
onDispose {
println("DisposableEffect: exited main")
}
}
}
Also note that in both cases, and in many other cases in compose, you pass key to these functions. This helps compose understand when the value should be recomputed. In my example it is Unit, which means that it won't change until the view is gone. But if you create a remember value, use another dynamic value from the view model, or pass another argument to composable, you can pass it as a key, this will cancel the current LaunchedEffect job and call onDispose for DisposableEffect, and your job will be restarted with the updated key value. You can pass as many keys as you want.
Read more about the state in Compose in documentation.
We want to write tests for a project that uses Spring Boot and Spring AMQP. As we code in Kotlin we would like to use MockK instead of Mockito as it better fits Kotlin code style and best practices.
The RabbitListenerTestHarness class provides some convienient feature for testing #RabbitListeners. However, it returns implementations of Mockito's Answer interface, which are incompatible with the Answer interface of MockK.
Is there a way to use the Mockito answers with MockK, e.g. some exisiting wrappers for interoperability?
Consider the following example listener:
class SampleListener {
#RabbitListener(id = "sampleReceiver", queues = ["testQueue"])
fun receiveMessage(message: Message) {
}
}
and the actual test:
#SpringBootTest
class SampleTest(#Autowired val template: TestRabbitTemplate) {
#Autowired
lateinit var testHarness: RabbitListenerTestHarness
#Test
fun testRabbit() {
val spy = testHarness.getSpy<SampleListener>("sampleReceiver")
val answer: LatchCountDownAndCallRealMethodAnswer = testHarness.getLatchAnswerFor("sampleReceiver", 1)
// Mockito.doAnswer(answer).`when`(spy).receiveMessage(ArgumentMatchers.any())
every { spy.receiveMessage(any()) } answers { /* what goes here? */ }
template.convertAndSend("testQueue", "test")
}
}
The test contains the Mockito call, as mentioned in the Docs, as comment.
My question is, how can I use the answer object, returned from getLatchAnswerFor to complete the MockK stub?
It's probably easier to not use the harness at all and add your own proxy around the message listener.
get the container from the RabbitListenerEndpointRegistry
get listener from the container, wrap it in a proxy and set it on the container
stop/start the container
send message(s)
So I have a utility class for validation with static methods like the following:
static validateX = { x ->
// some business logc
}
Now, I use this static closure in my command objects for custom validation:
class TestCommand {
String x
static constraints = {
x(validator: Utility.validateX)
}
}
The problem arises when I try to mock this custom validator during my unit tests. I have tried a bunch of things as seen below:
GroovyMock(Utility)
Utility.validateX(someVal) >> true
Utility.validateX.call(someVal) >> true
Utility.validateX >> { def x -> true }
But none of these work!
I have found a work around where I change static utility closure to a method. When I change it to a normal method, GroovyMock seems to work fine. But I would rather not do this since I'll then I have to use the ampersand symbol wherever I reference the method, and I can see it confusing others on my team.
So basically, my question is how do I mock a static closure in my unit tests?
Say I've got two taglibs, Foo which does something specific for a particular part of my application, and Util which is shared across the whole thing. I want to do something like this:
class UtilTagLib {
def utilTag = { attrs ->
...
}
}
class FooTagLib {
def fooTag = {
...
out << g.utilTag(att1: "att1", att2: "att2")
...
}
}
However, when I do this, and try to run my unit test for fooTag(), I get:
groovy.lang.MissingMethodException: No signature of method: org.codehaus.groovy.grails.web.pages.GroovyPage.utilTag() is applicable for argument types: (java.util.LinkedHashMap) values: [[att1:att1, att2:att2]]
I tried giving UtilTagLib its own namespace
static namespace = "util"
and changing the call to
out << util.utilTag(...)
but this just gets me
groovy.lang.MissingPropertyException: No such property: util for class: org.example.FooTagLib
Possibly also of note: In the log, I see:
WARN - Bean named 'groovyPagesUriService' is missing.
Obviously, UtilTagLib isn't getting created and injected correctly. How can I fix this?
Solution: add the call
mockTagLib UtilTagLib
to the setUp() (or #Before) method of the test case. This is a method on GroovyPageUnitTestMixin that, somewhat counterintuitively, instantiates the specified tag library -- the real one, not a mock -- and wires it into the Grails application context. It's used internally to set up the actual taglib under test (in this case FooTagLib), but it also works to set up additional collaborator tag libs.
Note that this isn't perfect, since it makes it more of an integration test than a pure unit test -- ideally we would be using a mock UtilTagLib and just testing the interaction.
One approach would be to refactor the line:
out << g.utilTag(att1: "att1", att2: "att2")
in to its own method, say "renderUtilTag(...)", then mock that out in the unit test, e.g.:
FooTagLib.metaClass.renderUtilTag = { /* something */ }
That way you're testing the functionality of FooTagLib only in the unit test, with no dependency on UtilTagLib.