I have a custom Image composable that uses Coil's rememberAsyncImagePainter.
However, I have another component that uses resources and has logic that is handled separately.
I successfully render the custom resource placeholder, however I'm not sure how I can write a test to check that the actual url image is loaded & visible.
Both the url image and the resource image have different testTags, however in the test, the node with the url image's tag never exists.
Does Coil have any solution to mock the ImageRequest.Builder so that I can guarantee that the URL image successfully loads?
I would prefer to not add any test-related code to the component itself, but if that's the only way, then I would prefer the component to be testable.
According to the official docs https://coil-kt.github.io/coil/image_loaders/#testing, you can create a FakeImageLoader class like this:
class FakeImageLoader(private val context: Context) : ImageLoader {
override val defaults = DefaultRequestOptions()
override val components = ComponentRegistry()
override val memoryCache: MemoryCache? get() = null
override val diskCache: DiskCache? get() = null
override fun enqueue(request: ImageRequest): Disposable {
// Always call onStart before onSuccess.
request.target?.onStart(request.placeholder)
val result = ColorDrawable(Color.BLACK)
request.target?.onSuccess(result)
return object : Disposable {
override val job = CompletableDeferred(newResult(request, result))
override val isDisposed get() = true
override fun dispose() {}
}
}
override suspend fun execute(request: ImageRequest): ImageResult {
return newResult(request, ColorDrawable(Color.BLACK))
}
private fun newResult(request: ImageRequest, drawable: Drawable): SuccessResult {
return SuccessResult(
drawable = drawable,
request = request,
dataSource = DataSource.MEMORY_CACHE
)
}
override fun newBuilder() = throw UnsupportedOperationException()
override fun shutdown() {}
}
And you UI test, you can write
#Test
fun testCustomImageComposable() {
Coil.setImageLoader(FakeCoilImageLoader())
setContent {
CutomImageComposable(...)
}
// ... assert image is displayed, etc.
}
This should guarantee the image to be shown every-time the test is executed.
Alternatively, you can mock ImageLoader and mimic the behavior above.
However, I would not recommend mocking ImageLoader, as we do not know whether rememberAsyncImagePainter uses all of the methods, but if you must mock it, it would be worth a try.
Related
i am using Jetpack Compose 1.2.0 and Room 2.4.3
everything works well and my state changes when i use Read, Insert, Delete but i don't know why it does not work with update (when i navigate back or re enter the screen Its okay and i will get updated data)
this is my DAO
#Dao
abstract class MessageDAO : BaseDao<Message>() {
#Query("SELECT * FROM messages")
abstract fun getAllMessages(): LiveData<List<Message>>
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract override fun insert(obj: Message): Long
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract override fun insert(obj: MutableList<Message>?): MutableList<Long>
#Update
abstract override fun update(obj: Message)
#Update
abstract override fun update(obj: MutableList<Message>?)
#Delete
abstract override fun delete(obj: Message)
#Query("delete from messages where id in (:messageIDs)")
abstract fun delete(messageIDs: List<Long>)
}
also this my viewModel
#HiltViewModel
class MessagesViewModel #Inject constructor(
private val application: Application,
private val messageRepository: MessageRepository
) : ViewModel() {
fun sendMessage(message: Message) =
CoroutineScope(Dispatchers.IO).launch {
message.localId = messageRepository.insert(message)
}
fun editMessage(message: Message) =
CoroutineScope(Dispatchers.IO).launch {
messageRepository.update(message)
}
fun deleteMessage(message: Message) {
CoroutineScope(Dispatchers.IO).launch {
messageRepository.delete(message)
}
}
}
and this is my Composable function to show data
#Composable
fun Messaging(
navController: NavController,
to_user_id: String,
messagesViewModel: MessagesViewModel,
currentUserId: Long,
) {
val messages: List<Message> by messagesViewModel.getConversationMessages(to_user_id.toLong())
.observeAsState(
listOf()
)
Column {
MessagingHeader(
navController,
profileViewModel,
to_user_id.toLong(),
selection
)
Column(Modifier.weight(1f)) {
Column(
Modifier.verticalScroll(
state = rememberScrollState(),
reverseScrolling = true
)
) {
messages.forEach { message ->
Message(
currentUserId,
message
)
}
}
}
}
UPDATE for getConversationMessages func:
fun getConversationMessages(targetUserId: Long) =
messageDAO.getMessagesByTargetUserId(targetUserId)
and this getMessagesByTargetUserId func for my MessageDAO
#Query("SELECT * FROM messages WHERE receiver = :targetUserId OR sender = :targetUserId ORDER BY createdAt")
abstract fun getMessagesByTargetUserId(targetUserId: Long): LiveData<List<Message>>
It sounds like the issue you're experiencing is that the data in your Composable function is not being updated when you call the "update" method in your ViewModel. The reason for this is that the LiveData returned by the "getConversationMessages" function in your repository is not being updated when you call "update" in your ViewModel.
One solution to this issue is to use the "postValue" method instead of "setValue" when updating the value of the LiveData object in your repository. When you call "postValue", it will trigger the observer in your Composable function and update the UI with the latest data.
Another solution is to use the Transformations.map method in your Messaging function to convert the LiveData returned by the getConversationMessages to another LiveData type, and then observe it, this way when the data change inside the map the observer will be triggered.
For example:
val conversationLiveData = messagesViewModel.getConversationMessages(to_user_id.toLong())
val messages: List<Message> by Transformations.map(conversationLiveData){it}.observeAsState(listOf())
It's also possible that the issue is caused by a problem with the Room database's ability to notify the observer when the data is updated. One way to check this is to add a log statement in the "update" method in your ViewModel to ensure that it is being called when you expect it to be.
It's also possible that the issue is related to the use of CoroutineScope with Dispatchers.IO in your ViewModel. In this case it might be useful to try calling the update method with Dispatchers.Main instead.
In summary, the issue you're experiencing is likely related to the LiveData object not being updated when the update method is called. There are several possible solutions to this issue, including using the "postValue" method instead of "setValue" when updating the value of the LiveData object, using Transformations.map, making sure the update method is being called when it should be, or trying to call the update method with Dispatchers.Main instead of Dispatchers.IO in your ViewModel.
I am trying to migrate some code using a Repository pattern from Vapor 3 to Vapor 4. I have gone through the documentation of this specific pattern from the Vapor 4 documentation, and I think I understand it for the most part.
The one thing I am not getting, however, is the way that the repository factory gets set within the Application extension. The example from the documentation shows this:
extension Application {
private struct UserRepositoryKey: StorageKey {
typealias Value = UserRepositoryFactory
}
var users: UserRepositoryFactory {
get {
self.storage[UserRepositoryKey.self] ?? .init()
}
set {
self.storage[UserRepositoryKey.self] = newValue
}
}
}
If I am reading the getter method correctly (and I might not be - I'm far from a Swift expert), a new instance of the UserRepositoryFactory structure will be created and returned when app.users is referenced. At that time, however, it does not appear that the contents of self.storage[UserRepositoryKey.self] is changed in any way. So if I happened to access app.users two times in a row, I would get 2 different instances returned to me and self.storage[UserRepositoryKey.self] would remain set to nil.
Following through the rest of the sample code in the document, it appears to define the make function that will be used by the factory when configuring the app as so:
app.users.use { req in
DatabaseUserRepository(database: req.db)
}
Here it seems like app.users.use would get a new factory instance and call its use function to set the appropriate make method for that instance.
Later, when I go to handle a request, I use the request.users method that was defined by this Request extension:
extension Request {
var users: UserRepository {
self.application.users.make!(self)
}
}
Here it seems like self.application.users.make would be invoked on a different repository factory instance that is referenced by self.application.users. It would therefore not apply the factory's make method that was set earlier when configuring the application.
So what am I missing here?
It looks like the docs are slightly out of date for that. You can have a look at how views or client is done, but somewhere you need to call initialize() to set the repository. Here's what my working repository looks like:
import Vapor
extension Application {
struct Repositories {
struct Provider {
let run: (Application) -> ()
public init(_ run: #escaping (Application) -> ()) {
self.run = run
}
}
final class Storage {
var makeRepository: ((Application) -> APIRepository)?
init() { }
}
struct Key: StorageKey {
typealias Value = Storage
}
let application: Application
var repository: APIRepository {
guard let makeRepository = self.storage.makeRepository else {
fatalError("No repository configured. Configure with app.repositories.use(...)")
}
return makeRepository(self.application)
}
func use(_ provider: Provider) {
provider.run(self.application)
}
func use(_ makeRepository: #escaping (Application) -> APIRepository) {
self.storage.makeRepository = makeRepository
}
func initialize() {
self.application.storage[Key.self] = .init()
}
private var storage: Storage {
if self.application.storage[Key.self] == nil {
self.initialize()
}
return self.application.storage[Key.self]!
}
}
var repositories: Repositories {
.init(application: self)
}
}
That autoinitializes itself the first time it's used. Note that APIRepository is the protocol used for my repostiory. FluentRepository is the Fluent implementation of that protocol. Then like you I have an extension on Request to use it in request handlers:
extension Request {
var repository: APIRepository {
self.application.repositories.repository.for(self)
}
}
Finally, you need to configure it to use the right repository. So in my configure.swift I have:
app.repositories.use { application in
FluentRepository(database: application.db)
}
and in tests I can switch it for the in-memory repository that doesn't touch the DB:
application.repositories.use { _ in
return inMemoryRepository
}
I have managed to get the example from the docs working as-is.
Tracing through the execution with the debugger, there is the predictable call to get, as you say, and this returns the instance from .init() as the failover from not having a previously stored value. Included in the example you refer to is:
struct UserRepositoryFactory {
var make: ((Request) -> UserRepository)?
mutating func use(_ make: #escaping ((Request) -> UserRepository)) {
self.make = make
}
}
This use function is executed next, which is mutating and updates the variable make. I believe it is this change to make that then triggers a call to set. It certainly happens immediately after use and before execution moves on in configure.swift. So, by the time the server formally starts and you actually use the Repository in a route, there is a stored instance that is reused as required.
So, I looked into mirror and they might be an option, but given their async nature they might be really awkward to use or just not viable in the long run. Since they are currently not supported (just a play-thing) they are not really viable at this time anyway.
Question: Given a series of Strings, eg. [ "Foo", "Bar" ] a base class Application and Widget in library corelib; and a corresponding class for each of the strings FooWidget, BarWidget in library applibrary;, what's currently the most elegant method to get Application to turn the strings into instances of the corresponding classes, that works with dart2js.
Equivalent PHP pseudo-example for clarity,
<?php # example
namespace corelib;
class Widget {
function name() {
return \get_called_class();
}
}
class Application {
static function resolve($name, $library) {
$class = $library.'\\'.$name.'Widget';
return new $class;
}
}
namespace applibrary;
class FooWidget extends \corelib\Widget {
// ...
}
class BarWidget extends \corelib\Widget {
// ...
}
$foowidget = \corelib\Application::resolve('Foo', 'applibrary');
$barwidget = \corelib\Application::resolve('Bar', 'applibrary');
echo "{$foowidget->name()} <br> {$barwidget->name()}";
Output
applibrary\FooWidget
applibrary\BarWidget
If you can validate the list of strings, then the best way for the moment (until mirror support in dart2js becomes better baked), is likely an if statement.
// toy implementation
Widget getWidget(name) {
switch (name) {
case "Foo": return new FooWidget();
case "Bar": return new FooWidget();
default: // handle error
}
}
// elsewhere:
var fooWidget = getWidget("Foo");
var barWidget = getWidget("Bar");
The list of xyzWidget classes will be a finite list (as you can't dynamically link in code at runtime anyway).
Of course, a more elegant implementation is to use mirrors (shown below, for reference, although it doesn't currently fulfil the dar2js criteria)
Future<Widget> getWidget(library, name) {
var completer = new Completer<Widget>();
MirrorSystem ms = currentMirrorSystem();
ClassMirror cm = ms.libraries[library].classes[name];
// instantiate an instance of the class
cm.newInstance(null,[]).then((instance) => completer.complete(instance));
return completer.future;
}
// elsewhere:
getWidget("applibrary","FooWidget").then((Widget widget) {
// do something with widget
});
I want to override a method definition in Grails. I am trying to use Groovy metaprogramming as the class which I want to override belongs to a framework.
Below is the original class.
class SpringSocialSimpleSignInAdapter implements SignInAdapter {
private RequestCache requestCache
SpringSocialSimpleSignInAdapter(RequestCache requestCache) {
this.requestCache = requestCache;
}
String signIn(String localUserId, Connection<?> connection, NativeWebRequest request) {
SignInUtils.signin localUserId
extractOriginalUrl request
}
}
I am trying to override like below
SpringSocialSimpleSignInAdapter.metaClass.signIn = {java.lang.String str, org.springframework.social.connect.Connection conn, org.springframework.web.context.request.NativeWebRequest webreq ->
println 'coming here....' // my implementation here
return 'something'
}
But for some reason overriding is not hapening. I am not able to figure it out. Any help would be greatly appretiated.
Thanks
Yeah, seems like that bug. I don't know your whole scenario, but anyway, here's a small workaround i made:
In your class definition, you don't implement the interface
You create your object and do your metamagic
Use groovy coercion to make it act as the interface and then you can pass it around
Here is a small script i made using JIRA bug to prove it:
interface I {
def doIt()
}
class T /*implements I*/ {
def doIt() { true }
}
def t = new T()
assert t.doIt()
t.metaClass.doIt = { -> false }
// here the coercion happens and the assertion works fine
def i = t as I
assert !i.doIt()
assert !t.doIt()
// here the polymorphism happens fine
def iOnlyAcceptInterface(I i) { assert !i.doIt() }
iOnlyAcceptInterface(i)
I don't know how to use binding with closures in Groovy. I wrote a test code and while running it, it said, missing method setBinding on the closure passed as parameter.
void testMeasurement() {
prepareData(someClosure)
}
def someClosure = {
assertEquals("apple", a)
}
void prepareData(testCase) {
def binding = new Binding()
binding.setVariable("a", "apple")
testCase.setBinding(binding)
testCase.call()
}
This works for me with Groovy 1.7.3:
someClosure = {
assert "apple" == a
}
void testMeasurement() {
prepareData(someClosure)
}
void prepareData(testCase) {
def binding = new Binding()
binding.setVariable("a", "apple")
testCase.setBinding(binding)
testCase.call()
}
testMeasurement()
In this script example, the setBinding call is setting a in the scripts binding (as you can see from the Closure documentation, there is no setBinding call). So after the setBinding call, you can call
println a
and it will print out "apple"
So to do this in the class, you can set the delegate for the closure (the closure will revert back to this delegate when a property cannot be found locally) like so:
class TestClass {
void testMeasurement() {
prepareData(someClosure)
}
def someClosure = { ->
assert "apple" == a
}
void prepareData( testCase ) {
def binding = new Binding()
binding.setVariable("a", "apple")
testCase.delegate = binding
testCase.call()
}
}
And it should grab the value fro a from the delegate class (in this case, a binding)
This page here goes through the usage of delegate and the scope of variables in Closures
Indeed, instead of using a Binding object, you should be able to use a simple Map like so:
void prepareData( testCase ) {
testCase.delegate = [ a:'apple' ]
testCase.call()
}
Hope it helps!
This is really strange behaviour: removing the "def" in front of someClosure declaration makes the script work in JDK1.6 Groovy:1.7.3
Update: This was posted in the answer above. My mistake to repeat it.
Update: Why it works? Without a def first line is taken as a property assignment which calls setProperty and makes the variable available in binding, which is resolved later.
a def should have worked as well as per (http://docs.codehaus.org/display/GROOVY/Groovy+Beans)
someClosure = {
assert "apple", a
print "Done"
}
void testMeasurement() {
prepareData(someClosure)
}
void prepareData(testCase) {
def binding = new Binding()
binding.setVariable("a", "apple")
testCase.setBinding(binding)
testCase.call()
}
testMeasurement()
I could reproduce the problem you mention by following code. But i am not sure if this is the correct way to use Binding. GroovyDocs says they are to be used with scripts. Could you point me to documentation which suggests such usage of Binding with Closures.
class TestBinding extends GroovyTestCase {
void testMeasurement() {
prepareData(someClosure)
}
def someClosure = {
assertEquals("apple", a)
}
void prepareData(testCase) {
def binding = new Binding()
binding.setVariable("a", "apple")
//this.setBinding(binding)
testCase.setBinding(binding)
testCase.call()
}
}
This was answered on groovy mailing list:
In a script, def foo will create a local variable, not a property
(private field + getter/setter).
Think of a script a bit like if it's the body of a run() or main() method.
That's where and how you can define local variables.