I'm implementing tests on my Android app and I want to do unit tests on a model. The problem is that I do not have setters as the model is created with Realm or by parsing a CSV file (witch univocity csv parser).
So, how could I create a mocked object with valid values? I have something like that:
public class Content {
private String title;
private String description;
...
}
How could I generate a mocked Content object with a title and description data?
Thanks in advance
Use code below in your test class:
Field field = Content.class.getDeclaredField("str");
field.setAccessible(true);
field.set(yourObject, "some value");
yourObject is a instance of Content that you use in your test class.
But you shouldn't fill mock object - you should just define method result for mock object.
A word of warning: reflection has a lot of disadvantages; for example a simple name change for your fields will go unnoticed; and not lead to compiler errors but to failing unit tests later on.
This I suggest a different solution - providing a package-private or protected constructor that you can use to initialize your fields. Then you do not to use reflection; and at the same time, your "public" interface of that class doesn't change either, like:
public class Content {
// unit testing only
Content(String title, ... ) { ...
But of course, you have to do balancing - either you add that constructor that isn't required for production; or you go with not so robust reflection code!
Related
I'm trying to use a room entity with a value class:
#JvmInline
value class UserToken(val token: String)
and the entity:
#Entity(tableName = TABLE_AUTH_TOKEN)
data class TokenEntity(
#PrimaryKey val id: Int = 0,
val token: UserToken
)
I get the following error:
error: Entities and POJOs must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type).
public final class TokenEntity {
^
is it even possible to use room with value class? I couldn't find anything about this. thanks
See the comment from #CommonsWare. Android does not yet support value classes for Room.
The same holds true for the value classes introduced in kotlin 1.5. The type is not supported.
— Support Inline class in Room entity
Here is a possible explanation according to Kotlin Inline Classes in an Android World.
Looking to solve this you could try and add a TypeConverter for your Inline class, but since your Inline class is just the value it wraps when it’s compiled, this doesn’t make much sense and it doesn’t work as you’d expect even if you tried...
I’m just guessing it’s because this is a TypeConverter converting UserId to Int which is basically the same as Int to Int 😭. Someone will probably solve this problem, but if you have to create a TypeConverter for your Inline class then you are still plus one class for the count (multidex). 👎
I think yes if you can provide a type converter for it to change it to some sort of primitive data type (int , string, long ...etc) when it needs to be stored, and to change it back to its class type when it's fetched from database.
You can read about Type Converters from here
Referencing complex data using Room
other than that, your other class should be an entity and bind both your entities together using a Relation.
at least that's what I know about how to use Room.
UserToken always will have only one attribute? In this case, you don't need two classes, just use token: String directly on your entity class;
If you really need keep this class, you have two options:
TypeConverter, where you basically will convert the object into a json, and save as string in the database;
Relation, where you will transform the UserToken in a entity, and on TokenEntity save the tokenId.
I use kotlinx.serialization on Kotlin native project, I a defined Super class for my models and all of the models extends from it.
I defined a function to called toJSON() for serialize variables and fields inside model that all of class models have it.
#Serializable
open class Model {
fun toJSON(): String = JSON.stringify(this);
}
And I created a subclass
class Me : Model() {
var name:String = "Jack";
}
but when I invoke JSON.stringify(this), IDE get a Warning to me:
This declaration is experimental and its usage must be marked with '#kotlinx.serialization.ImplicitReflectionSerializer' or '#UseExperimental(kotlinx.serialization.ImplicitReflectionSerializer::class)'
I paid attention and I used #ImplicitReflectionSerializer annotation while not worked.
Where is my problem?
This is discussed here. It's the particular overload you're using which is still experimental. So your options are either to use the other overload (which takes in a serializer) or to use one of the annotations mentioned in the error message. If you look at the answer to the question I linked (and the comments following it), you'll see it talks about using #UseExperimental and where it should be used.
My server is sending me a list of object type Parent, as List<Parent> parents. I need to save this list in Room and to do so I need a PrimaryKey. So what I try to do in room is create something like
#Entity
class Child(mApple:String,mOrange:String):Parent(mApple,mOrange){
#PrimaryKeyy(autoGenerate=true)
var id:Int=0
}
And here is what parent looks like:
//it's a java class and so to be complete I will recreate it in Java
class Parent{
private final String mApple;
private final String mOrange;
private final String mDerivative;
public Parent(String apple, String orange){
mApple=apple;
mOrange=orange;
mDerivation = someMagic(apple,orange)
}
//then a bunch of methods for dealing with the state variables
}
The reason I want to extend Child from Parent is because it makes working with both Child and Parent together easy. But room is not compiling and I suspect it has to do with this. Unfortunately, Kotlin is not pointing me to where the error is occuring.
Is the error really due to the fact that Parent is not an Entity and that derivative does not provide a setter?
How do I get Android Studio to show me more details about this bug, such as line number? (linked to related questions)
The only errors I am able to get from the build tab:
error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
error: Cannot find getter for field.
error: Cannot find setter for field.
error: Cannot figure out how to read this field from a cursor.
error: Cannot find getter for field.
error: Cannot find setter for field.
Why is it not possible to define generic binding conversion within android data binding library?
#BindingConversion
public static <T> T convertMyClass(MyClass<T> obj) {
return obj.get();
}
With this method I am getting can not find the setter for attribute 'android:text' with parameter type com.example.MyClass<java.lang.String> error. Defining explicit types works alright.
I was trying to find the way ObservableField<T> is getting converted but didn't succeed. Does anyone know how is this happening? Is there anything I'm doing wrong?
In two words: type erasure.
Generics are a double edged sword that cuts out some of the run time capability of the type system in exchange for compile time checks. You're telling the compiler to rewrite code to make these type conversions "just work." The trade-off is that it has to turn generic class references like "T" into just "Object". So the signature of your method after compilation is
Object convertMyClass(MyClass)
The data binding system is looking for a return type "String". And so doesn't even consider your method.
The data binding system could probably be made smarter, to be able to recognize your BindingConversion, but I wouldn't hold my breath for that feature.
Here is some bash which illustrates type erasure.
$ echo 'public class A{ public <T> T deRef(java.util.concurrent.atomic.AtomicReference<T> atom) {return atom.get();} }' >A.java
$ javac A.java
$ groovy -e 'println A.class.getMethod("deRef", java.util.concurrent.atomic.AtomicReference.class)'
public java.lang.Object A.deRef(java.util.concurrent.atomic.AtomicReference)
That last line of output is the method signature for the generic method.
A work-around would be to subclass MyClass with specific parameterized subclasses like so:
public class MyStringClass extends MyClass<String> {
#Override
public String get() {
return super.get();
}
#BindingConversion
public static String convertMyClass(MyStringClass obj) {
return obj.get();
}
}
Regarding ObservableField, it doesn't need the BindingConversion mechanism because the data-binding library references it in the java code, and therefore compile-time generics checking does the job of matching types up.
I try to use 2 libraries:
square/Retrofit - Rest client
satyan/sugar - db orm
retrofit use gson, so do class
public class Book{
String name;
public Book(String name) {
this.name = name;
}
}
ok, retrofit succesfully get data from server and put in our Book class.
now i want save this data. for use orm need extend parent class
public class Book extends SugarRecord<Book>{
String name;
public Book(String name) {
this.name = name;
}
}
but after extend the parent class, retrofit cannot parse json.
so we get an error:
java.lang.RuntimeException: An error occured while executing doInBackground()
...
Caused by: retrofit.RetrofitError: java.lang.StackOverflowError at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:390)
...
Caused by: java.lang.StackOverflowError at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:375)
...
how to make friends 2 libraries that they use one object class?
or how to specify the retrofit, so that it did not touch Book's class parent?
Error happens when Gson tries to resolve type information about an object it have to deserialize. It gets into an infinite recursion due to the cyclic reference to your Book class in extends declaration.
However even if Gson could cope with your class, I would not recommend using these libs combination.
You see, what Gson does is much alike to what standard Java serialization does but in JSON format. I mean that Gson takes the internal state of your object and performs its serialization. And when it parses JSON it creates an object with the state specified in this JSON.
If you take a look at SugarRecord class, you'll see that it has a field named "tableName". Thus if you passed your Book object to Gson instance, you'd get
{name: "book name", tableName: "table_book"}.
Moreover, if you got a response from server which is like
{name: "another book name", tableName: "something absolutely unrelated"},
you would get an instance of Book with a state exactly matching what is described in this response. Meaning, with tableName being not equal to what you would like...
You could workaround this issue using exclusion strategies in Gson, but overall you'll get yet another problem.
P.S. After a quick look at SugarRecord class on github I do not understand why it has a type parameter at all. It's even not used really. Thus technically I think you'll be able to combine these 2 libraries, if you skip type parameter in extends declaration making your class look like class Book extends SugarRecod { }, and use an exclusion strategy. Yet, I wouldn't do it myself in practice :).
Your POJO class need a empty constructor :
you shoud add this constructor to your Book class:
public Book(){
}