I use Junit4 and kotlin.
I use the Enclosed for the inner class test.
import org.junit.Test
import org.junit.experimental.runners.Enclosed
import org.junit.runner.RunWith
#RunWith(Enclosed::class)
class SampleTest {
var text = "parent class"
class Class1 {
#Test
fun `print the text`() {
println(text)
}
}
inner class Class2 {
#Test
fun `print the text`() {
println(text)
}
}
}
in Class1 and Class2, need the text variable.
I use the inner for an access child class to parent class.
but I have a problem, the test function is removed and I can't test that. see the photo.
can I test the inner class in kotlin with Junit4?
Class1 is a plain nested class that happens to be in SampleTest. It's just an organisational thing, your Class1 instance doesn't have any reference to a SampleTest instance, so it can't access text (not without being passed an instance explicitly).
If you want a nested class to be able to access an enclosing instance like that, you need to mark it as inner, like with Class2. That way you can create an instance of Class2 through an instance of SampleClass, like
val sample = SampleClass()
val class2 = sample.Class2()
sample.text = "wow!"
class2.`print the text`()
You can read about this stuff in the docs if it's unfamiliar
So yeah, if JUnit constructs a Class1 instance, it doesn't know what text is, since that's an instance variable in some other unrelated class.
And I'm assuming it doesn't know how to create Class2, since it requires a SampleTest instance to do that. All the examples for Enclosed use static nested classes, and like the Java docs say:
A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class. In effect, a static nested class is behaviorally a top-level class that has been nested in another top-level class for packaging convenience.
so that's basically the same as Kotlin's (non-inner) nested classes, like your Class1. You probably can't use inner classes (not to organise and automatically run tests, anyway)
You probably want to stick text in a companion object if you're organising your tests in classes (or just a top-level object outside of SampleClass, or just in the top level of the file if you really want - just somewhere it can be accessed by any other class in a "static" way
we can use 3 wayes
use NestedRunner this
use HierarchicalContextRunner this
use Enclosed and the companion object and don't use the inner class
#RunWith(Enclosed::class)
class TEST {
companion object {
var text = "parent class"
}
class Class1 {
#Test
fun `print the text`() {
println(text)
}
}
class Class2 {
#Test
fun `print the text`() {
println(text)
}
}
}
Related
I'm not very clear about the best way to inject into a static methods helper class (lets say a Custom class).
I'm kinda new to Kotlin, and as I've learnt we can access a method statically in two ways:
Object class.
Class + companion object.
To start, I'm not sure which one is the most recommended one (if there is a best practice regarding this), but my "problem" arises when needing to inject dependencies into a static method class.
Let's go with a simple example:
I have a static methods class called AWUtils (not decided if it should be an object class or a class with companion object yet though, and this will most likely depend on the injection mechanism recommended) with the next method:
fun setAmpersand2Yellow(text2Replace: String, target: String): String {
return text2Replace.replace(
target, "<span style=\"color:" +
app.drawerFooterColor + ";\">" + target + "</span>"
)
}
Here, app is the instance of my AppSettings class which holds all app configuration so, as you see setAmpersand2Yellow needs AppSettings, and of course I would't pass it as a parameter by any means, so it's a AWUtils dependence.
Using AWUtils as a class with companion object for the static methods I cannot inject directly AppSettings into company object as far as I know (at least I cannot do constructor injection, let me know if I'm wrong) and if I inject into companion object parent class (AWUtils) constructor then I don't know how to access those dependences from the companion object itself (the child).
If I use fields injection in AWUtils as a class then it complains than lateinit field has not been initialised and I don't know how to deal with this, because as far as I know lateinit fields are initialised in onCreate, which does not exist in this kind of classes.
One other possibility is to use an object with fields and set the dependencies values from caller in a static way before calling the method, for example:
object AWUtils {
var app: AppSettings? = null
fun setAmpersand2Yellow(text2Replace: String, target: String): String {
return text2Replace.replace(
target, "<span style=\"color:" +
app.drawerFooterColor + ";\">" + target + "</span>"
)
}
}
#AndroidEntryPoint
class OtherClass
#Inject constructor(private val app: AppSettings) {
fun AnyFunction() {
var mystr = "whatever"
AWUtils.app = app
var yellowStr = AWUtils.setAmpersand2Yellow(myStr)
}
}
In the end, I'm not sure on how to supply dependencies to a static methods class and which form of "static" class should I choose.
Edit 1:
Apart from my ApSettings class, I need a context, like for example in this next isTablet method:
val isTablet: String
get() {
return ((context.resources.configuration.screenLayout
and Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_LARGE)
}
In the end, I need a context and my AppSettings (or any other custom classes) to be injected anyway in a class with static methods.
Edit 2:
I could do (from the activity):
AWUtils.context = this
AWUtils.app = app
var isTablet = AWUtils.isTablet
And it works, but rather to be in the need of assigning a value to two fields (or more) every time I need to call a static method, I would prefer the fields to be injected in any way.
That's what dependency injection is meant for, isn't it?
Edit 3: I'm starting to be fed up with Hilt, what is supposed would have been created to simplify our life, only makes our programming life much more complicated.
As you clarified in the comments, you want to have your utils class accessible in an easy way across your codebase, so this answer will focus on that and on your original questions.
I'm kinda new to Kotlin, and as I've learnt we can access a method statically in two ways: Object class or Class + companion object.
Kotlin does not have Java-style statics. One reasoning behind it was to encourage more maintainable coding practices. Static methods and static classes are also a nightmare for testing your code.
In Kotlin you would go with an object (but a class + companion object would work in the same way)
object AWUtils {
lateinit var appContext: Context
lateinit var appSettings: AppSettings
fun initialize(
appContext: Context,
appSettings: AppSettings,
// more dependencies go here
) {
this.appContext = appContext
this.appSettings = appSettings
// and initialize them here
}
val isTablet: Boolean
get() = ((appContext.resources.configuration.screenLayout
and Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_LARGE)
fun setAmpersand2Yellow(text2Replace: String, target: String): String {
return text2Replace.replace(
target, "<span style=\"color:" +
appSettings.drawerFooterColor + ";\">" + target + "</span>"
)
}
}
Since this object should be accessible across the whole application it should be initialized as soon as possible, so in Application.onCreate
#HiltAndroidApp
class Application : android.app.Application() {
// you can inject other application-wide dependencies here
// #Inject
// lateinit var someOtherDependency: SomeOtherDependency
override fun onCreate() {
super.onCreate()
// initialize the utils singleton object with dependencies
AWUtils.initialize(applicationContext, AppSettings())
}
Now anywhere in your app code you can use AWUtils and AppSettings
class OtherClass { // no need to inject AppSettings anymore
fun anyFunction() {
val mystr = "whatever"
val yellowStr = AWUtils.setAmpersand2Yellow(myStr)
// This also works
if (AWUtils.isTablet) {
// and this as well
val color = AWUtils.appSettings.drawerFooterColor
}
}
}
There is another way in Kotlin to write helper/util functions, called extension functions.
Your isTablet check might be written as an extension function like this
// This isTablet() can be called on any Configuration instance
// The this. part can also be omitted
fun Configuration.isTablet() = ((this.screenLayout
and Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_LARGE)
// This isTablet() can be called on any Resources instance
fun Resources.isTablet() = configuration.isTablet()
// This isTablet() can be called on any Context instance
fun Context.isTablet() = resources.isTablet()
With the above extension functions in place the implementation inside AWUtils would be simplified to
val isTablet: Boolean
get() = appContext.isTablet()
Inside (or on a reference of) any class that implements Context, such as Application, Activity, Service etc., you can then simply call isTablet()
class SomeActivity : Activity() {
fun someFunction() {
if (isTablet()) {
// ...
}
}
}
And elsewhere where Context or Resources are available in some way, you can simply call resources.isTablet()
class SomeFragment : Fragment() {
fun someFunction() {
if (resources.isTablet()) {
// ...
}
}
}
Edit 3: I'm starting to be fed up with Hilt, what is supposed would have been created to simplify our life, only makes our programming life much more complicated.
Yeah, Hilt is focusing on constructor injection and can only do field injection out-of-the-box in very limited cases, afaik only inside Android classes annotated with #AndroidEntryPoint and inside the class extending the Application class when annotated with #HiltAndroidApp.
Docs for #AndroidEntryPoint say
Marks an Android component class to be setup for injection with the standard Hilt Dagger Android components. Currently, this supports activities, fragments, views, services, and broadcast receivers.
If you feel that you need a lot of field injection, because you are working with "static"-like objects in Kotlin, consider using Koin instead of Hilt for your next project.
I would like to create a unit test of this class but I would like the instance of the countryRepository: CountryRepository to be a mock in the case of the test.
I could create an alternative constructor to pass another instance but it doesn't seem like a great solution
How could I do?
class CountryListModel {
var countryRepository:CountryRepository = CountryRepositoryImp()
}
Pass it inside the constructor with default value:
class CountryListModel(val countryRepository:CountryRepository = CountryRepositoryImp())
I have a class O and a nested class inside of O called N.
In a function of NI want to reference this of O by using this#O.
But it does not recognize O, only when I use inner class.
However, if I use inner classes, android-studio suggests that this might lead to leaks.
Is there another way to reference the outer class or avoid leaks?
The memory leak possibility is caused by the fact that each instance of an inner class holds a reference to an instance of the outer class. That outer class instance may not be needed by the program logic, but it is still visible and thus is not subject to garbage collection.
So, if you are aware that the nested class instance may not need the whole content of the outer class instance for its logic, you can ensure that there's no memory leaks by not using inner classes.
If you still need some parts of the outer class instance, you can pass those values to the nested class instance manually:
class A {
val b: B = someB()
val c: C = someC()
// D uses C but does not need B, so we pass C to the constructor:
class D(private val c: C) { /* ... */ }
fun createD(): D = D(c)
}
If you also need the nested class instance to observe the changes of the outer class instance property (not just using a snapshot of the property at the nested class instance construction time), you may manually wrap that property into a reference holder and pass that holder to the nested class constructor:
class A {
val b: B = someB()
private val cHolder = CHolder(someC())
class CHolder(var c: C)
var c: C
get() = cHolder.c
set(value) { cHolder.c = value }
// D uses C but does not need B, so we pass C to the constructor:
class D(private val cHolder: CHolder) { /* ... */ }
fun createD(): D = D(cHolder)
}
Instead of the class CHolder, you may want to use some generic solution if this pattern repeats in your code; this is just a demo.
Then, if you want to reference the whole instance of the outer class, there's still an option to pass it to the nested class constructor. Compared to inner class, this allows you to manually control the lifetime of the outer instance and drop the reference to it once it is not needed:
class A {
class D(private var outer: A?) {
fun forgetOuterInstance() {
outer = null
}
}
fun createD(): D = D(this)
}
And finally, if your nested class needs the outer class instance during all of its lifetime, or if the outer class does not hold any expensive resources and you can deal with the potentially longer lifetime of its instances, then it's OK to use an inner class, just keep in mind that the outer class instance will stay alive as long is the inner class instance does. Due to this, you may want to move some resources out of the outer class to hold and release them in a more granular way.
I have a Kotlin class with companion object which sees some fields of the parent class and does not see others. There is no option in Android Studio to import.
class A{
var a = 1
var b = 2
companion object {
a += 1// visible and imported
b += 1// unresolved reference
}
}
I do not want to create this variable inside the companion object.
You are absolutely incorrect.
You cannot access members of class inside companion object at all. But you can use companion`s members in your class.
If you see kotlin bytecode you will see that Companion object compiles to
public static final class Companion {
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
Since Companion is static class it can exist without class where it is declared.
So in your case you can not access a and b because probably they are not exists.
They are not accessable for you too, but probably you cought IDE bug and it doesnt give you error
You cannot access instance variables from static context (companion), this is the same as Java code
Android Studio imported A class variables. In imports i see import package.A.a, but not import package.A.b
import package.A.a simply doesn't make sense for a class property a, and companion object wouldn't require an import from the class it's companion to anyway. My best guess is that it's importing from an object in a different package.
I have a data class in Kotlin that inherits from a Java class, which defines a constructor with 1 argument,
public BaseClass(String userSessionId) {
this.userSessionId = userSessionId;
}
My Kotlin class is defined as this
class DerivedClass(
userSessionId: String,
var other: Other? = null
) : BaseClass(userSessionId) {
I can't define it as a data class because of userSessionId, which Kotlin requires to be a val or var in data classes. However, if I do so, then Retrofit throws an exception because there are 2 members named userSessionId. Is there a way to have a data class inherit from a Java class with a constructor taking arguments? Note that I cannot change the base class.
A possible solution is to define a dummy val to avoid the name clash, but this is less than ideal
data class DerivedClass(
val dummy: String,
var other: Other? = null
) : BaseClass(dummy) {
You can use the transient keyword in Java to ignore a field during serialization, this can be done in Kotlin by using the #Transient annotation on the property instead.